mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-23 12:44:05 -08:00
Add service discovery for OvhCloud (#10802)
* feat(ovhcloud): add ovhcloud management Signed-off-by: Marine Bal <marine.bal@corp.ovh.com> Co-authored-by: Arnaud Sinays <sinaysarnaud@gmail.com>
This commit is contained in:
parent
e576103d7e
commit
16c3aa75c0
|
@ -47,6 +47,7 @@ import (
|
|||
"github.com/prometheus/prometheus/discovery/moby"
|
||||
"github.com/prometheus/prometheus/discovery/nomad"
|
||||
"github.com/prometheus/prometheus/discovery/openstack"
|
||||
"github.com/prometheus/prometheus/discovery/ovhcloud"
|
||||
"github.com/prometheus/prometheus/discovery/puppetdb"
|
||||
"github.com/prometheus/prometheus/discovery/scaleway"
|
||||
"github.com/prometheus/prometheus/discovery/targetgroup"
|
||||
|
@ -940,6 +941,35 @@ var expectedConf = &Config{
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
JobName: "ovhcloud",
|
||||
|
||||
HonorTimestamps: true,
|
||||
ScrapeInterval: model.Duration(15 * time.Second),
|
||||
ScrapeTimeout: DefaultGlobalConfig.ScrapeTimeout,
|
||||
HTTPClientConfig: config.DefaultHTTPClientConfig,
|
||||
MetricsPath: DefaultScrapeConfig.MetricsPath,
|
||||
Scheme: DefaultScrapeConfig.Scheme,
|
||||
|
||||
ServiceDiscoveryConfigs: discovery.Configs{
|
||||
&ovhcloud.SDConfig{
|
||||
Endpoint: "ovh-eu",
|
||||
ApplicationKey: "testAppKey",
|
||||
ApplicationSecret: "testAppSecret",
|
||||
ConsumerKey: "testConsumerKey",
|
||||
RefreshInterval: model.Duration(60 * time.Second),
|
||||
Service: "vps",
|
||||
},
|
||||
&ovhcloud.SDConfig{
|
||||
Endpoint: "ovh-eu",
|
||||
ApplicationKey: "testAppKey",
|
||||
ApplicationSecret: "testAppSecret",
|
||||
ConsumerKey: "testConsumerKey",
|
||||
RefreshInterval: model.Duration(60 * time.Second),
|
||||
Service: "dedicated_server",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
JobName: "scaleway",
|
||||
|
||||
|
@ -1175,7 +1205,7 @@ func TestElideSecrets(t *testing.T) {
|
|||
yamlConfig := string(config)
|
||||
|
||||
matches := secretRe.FindAllStringIndex(yamlConfig, -1)
|
||||
require.Equal(t, 18, len(matches), "wrong number of secret matches found")
|
||||
require.Equal(t, 22, len(matches), "wrong number of secret matches found")
|
||||
require.NotContains(t, yamlConfig, "mysecret",
|
||||
"yaml marshal reveals authentication credentials.")
|
||||
}
|
||||
|
@ -1618,6 +1648,14 @@ var expectedErrors = []struct {
|
|||
filename: "ionos_datacenter.bad.yml",
|
||||
errMsg: "datacenter id can't be empty",
|
||||
},
|
||||
{
|
||||
filename: "ovhcloud_no_secret.bad.yml",
|
||||
errMsg: "application secret can not be empty",
|
||||
},
|
||||
{
|
||||
filename: "ovhcloud_bad_service.bad.yml",
|
||||
errMsg: "unknown service: fakeservice",
|
||||
},
|
||||
}
|
||||
|
||||
func TestBadConfigs(t *testing.T) {
|
||||
|
|
15
config/testdata/conf.good.yml
vendored
15
config/testdata/conf.good.yml
vendored
|
@ -349,6 +349,21 @@ scrape_configs:
|
|||
eureka_sd_configs:
|
||||
- server: "http://eureka.example.com:8761/eureka"
|
||||
|
||||
- job_name: ovhcloud
|
||||
ovhcloud_sd_configs:
|
||||
- service: vps
|
||||
endpoint: ovh-eu
|
||||
application_key: testAppKey
|
||||
application_secret: testAppSecret
|
||||
consumer_key: testConsumerKey
|
||||
refresh_interval: 1m
|
||||
- service: dedicated_server
|
||||
endpoint: ovh-eu
|
||||
application_key: testAppKey
|
||||
application_secret: testAppSecret
|
||||
consumer_key: testConsumerKey
|
||||
refresh_interval: 1m
|
||||
|
||||
- job_name: scaleway
|
||||
scaleway_sd_configs:
|
||||
- role: instance
|
||||
|
|
8
config/testdata/ovhcloud_bad_service.bad.yml
vendored
Normal file
8
config/testdata/ovhcloud_bad_service.bad.yml
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
scrape_configs:
|
||||
- ovhcloud_sd_configs:
|
||||
- service: fakeservice
|
||||
endpoint: ovh-eu
|
||||
application_key: testAppKey
|
||||
application_secret: testAppSecret
|
||||
consumer_key: testConsumerKey
|
||||
refresh_interval: 1m
|
7
config/testdata/ovhcloud_no_secret.bad.yml
vendored
Normal file
7
config/testdata/ovhcloud_no_secret.bad.yml
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
scrape_configs:
|
||||
- ovhcloud_sd_configs:
|
||||
- service: dedicated_server
|
||||
endpoint: ovh-eu
|
||||
application_key: testAppKey
|
||||
consumer_key: testConsumerKey
|
||||
refresh_interval: 1m
|
|
@ -33,6 +33,7 @@ import (
|
|||
_ "github.com/prometheus/prometheus/discovery/moby" // register moby
|
||||
_ "github.com/prometheus/prometheus/discovery/nomad" // register nomad
|
||||
_ "github.com/prometheus/prometheus/discovery/openstack" // register openstack
|
||||
_ "github.com/prometheus/prometheus/discovery/ovhcloud" // register ovhcloud
|
||||
_ "github.com/prometheus/prometheus/discovery/puppetdb" // register puppetdb
|
||||
_ "github.com/prometheus/prometheus/discovery/scaleway" // register scaleway
|
||||
_ "github.com/prometheus/prometheus/discovery/triton" // register triton
|
||||
|
|
161
discovery/ovhcloud/dedicated_server.go
Normal file
161
discovery/ovhcloud/dedicated_server.go
Normal file
|
@ -0,0 +1,161 @@
|
|||
// 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 ovhcloud
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/ovh/go-ovh/ovh"
|
||||
"github.com/prometheus/common/model"
|
||||
|
||||
"github.com/prometheus/prometheus/discovery/refresh"
|
||||
"github.com/prometheus/prometheus/discovery/targetgroup"
|
||||
)
|
||||
|
||||
const (
|
||||
dedicatedServerAPIPath = "/dedicated/server"
|
||||
dedicatedServerLabelPrefix = metaLabelPrefix + "dedicatedServer_"
|
||||
)
|
||||
|
||||
type dedicatedServer struct {
|
||||
State string `json:"state"`
|
||||
ips []netip.Addr
|
||||
CommercialRange string `json:"commercialRange"`
|
||||
LinkSpeed int `json:"linkSpeed"`
|
||||
Rack string `json:"rack"`
|
||||
NoIntervention bool `json:"noIntervention"`
|
||||
Os string `json:"os"`
|
||||
SupportLevel string `json:"supportLevel"`
|
||||
ServerID int64 `json:"serverId"`
|
||||
Reverse string `json:"reverse"`
|
||||
Datacenter string `json:"datacenter"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type dedicatedServerDiscovery struct {
|
||||
*refresh.Discovery
|
||||
config *SDConfig
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
func newDedicatedServerDiscovery(conf *SDConfig, logger log.Logger) *dedicatedServerDiscovery {
|
||||
return &dedicatedServerDiscovery{config: conf, logger: logger}
|
||||
}
|
||||
|
||||
func getDedicatedServerList(client *ovh.Client) ([]string, error) {
|
||||
var dedicatedListName []string
|
||||
err := client.Get(dedicatedServerAPIPath, &dedicatedListName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dedicatedListName, nil
|
||||
}
|
||||
|
||||
func getDedicatedServerDetails(client *ovh.Client, serverName string) (*dedicatedServer, error) {
|
||||
var dedicatedServerDetails dedicatedServer
|
||||
err := client.Get(path.Join(dedicatedServerAPIPath, url.QueryEscape(serverName)), &dedicatedServerDetails)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ips []string
|
||||
err = client.Get(path.Join(dedicatedServerAPIPath, url.QueryEscape(serverName), "ips"), &ips)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parsedIPs, err := parseIPList(ips)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dedicatedServerDetails.ips = parsedIPs
|
||||
return &dedicatedServerDetails, nil
|
||||
}
|
||||
|
||||
func (d *dedicatedServerDiscovery) getService() string {
|
||||
return "dedicated_server"
|
||||
}
|
||||
|
||||
func (d *dedicatedServerDiscovery) getSource() string {
|
||||
return fmt.Sprintf("%s_%s", d.config.Name(), d.getService())
|
||||
}
|
||||
|
||||
func (d *dedicatedServerDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) {
|
||||
client, err := createClient(d.config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var dedicatedServerDetailedList []dedicatedServer
|
||||
dedicatedServerList, err := getDedicatedServerList(client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, dedicatedServerName := range dedicatedServerList {
|
||||
dedicatedServer, err := getDedicatedServerDetails(client, dedicatedServerName)
|
||||
if err != nil {
|
||||
err := level.Warn(d.logger).Log("msg", fmt.Sprintf("%s: Could not get details of %s", d.getSource(), dedicatedServerName), "err", err.Error())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
dedicatedServerDetailedList = append(dedicatedServerDetailedList, *dedicatedServer)
|
||||
}
|
||||
var targets []model.LabelSet
|
||||
|
||||
for _, server := range dedicatedServerDetailedList {
|
||||
var ipv4, ipv6 string
|
||||
for _, ip := range server.ips {
|
||||
if ip.Is4() {
|
||||
ipv4 = ip.String()
|
||||
}
|
||||
if ip.Is6() {
|
||||
ipv6 = ip.String()
|
||||
}
|
||||
}
|
||||
defaultIP := ipv4
|
||||
if defaultIP == "" {
|
||||
defaultIP = ipv6
|
||||
}
|
||||
labels := model.LabelSet{
|
||||
model.AddressLabel: model.LabelValue(defaultIP),
|
||||
model.InstanceLabel: model.LabelValue(server.Name),
|
||||
dedicatedServerLabelPrefix + "state": model.LabelValue(server.State),
|
||||
dedicatedServerLabelPrefix + "commercialRange": model.LabelValue(server.CommercialRange),
|
||||
dedicatedServerLabelPrefix + "linkSpeed": model.LabelValue(fmt.Sprintf("%d", server.LinkSpeed)),
|
||||
dedicatedServerLabelPrefix + "rack": model.LabelValue(server.Rack),
|
||||
dedicatedServerLabelPrefix + "noIntervention": model.LabelValue(strconv.FormatBool(server.NoIntervention)),
|
||||
dedicatedServerLabelPrefix + "os": model.LabelValue(server.Os),
|
||||
dedicatedServerLabelPrefix + "supportLevel": model.LabelValue(server.SupportLevel),
|
||||
dedicatedServerLabelPrefix + "serverId": model.LabelValue(fmt.Sprintf("%d", server.ServerID)),
|
||||
dedicatedServerLabelPrefix + "reverse": model.LabelValue(server.Reverse),
|
||||
dedicatedServerLabelPrefix + "datacenter": model.LabelValue(server.Datacenter),
|
||||
dedicatedServerLabelPrefix + "name": model.LabelValue(server.Name),
|
||||
dedicatedServerLabelPrefix + "ipv4": model.LabelValue(ipv4),
|
||||
dedicatedServerLabelPrefix + "ipv6": model.LabelValue(ipv6),
|
||||
}
|
||||
targets = append(targets, labels)
|
||||
}
|
||||
|
||||
return []*targetgroup.Group{{Source: d.getSource(), Targets: targets}}, nil
|
||||
}
|
123
discovery/ovhcloud/dedicated_server_test.go
Normal file
123
discovery/ovhcloud/dedicated_server_test.go
Normal file
|
@ -0,0 +1,123 @@
|
|||
// 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 ovhcloud
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func TestOvhcloudDedicatedServerRefresh(t *testing.T) {
|
||||
var cfg SDConfig
|
||||
|
||||
mock := httptest.NewServer(http.HandlerFunc(MockDedicatedAPI))
|
||||
defer mock.Close()
|
||||
cfgString := fmt.Sprintf(`
|
||||
---
|
||||
service: dedicated_server
|
||||
endpoint: %s
|
||||
application_key: %s
|
||||
application_secret: %s
|
||||
consumer_key: %s`, mock.URL, ovhcloudApplicationKeyTest, ovhcloudApplicationSecretTest, ovhcloudConsumerKeyTest)
|
||||
|
||||
require.NoError(t, yaml.UnmarshalStrict([]byte(cfgString), &cfg))
|
||||
d, err := newRefresher(&cfg, log.NewNopLogger())
|
||||
require.NoError(t, err)
|
||||
ctx := context.Background()
|
||||
targetGroups, err := d.refresh(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 1, len(targetGroups))
|
||||
targetGroup := targetGroups[0]
|
||||
require.NotNil(t, targetGroup)
|
||||
require.NotNil(t, targetGroup.Targets)
|
||||
require.Equal(t, 1, len(targetGroup.Targets))
|
||||
|
||||
for i, lbls := range []model.LabelSet{
|
||||
{
|
||||
"__address__": "1.2.3.4",
|
||||
"__meta_ovhcloud_dedicatedServer_commercialRange": "Advance-1 Gen 2",
|
||||
"__meta_ovhcloud_dedicatedServer_datacenter": "gra3",
|
||||
"__meta_ovhcloud_dedicatedServer_ipv4": "1.2.3.4",
|
||||
"__meta_ovhcloud_dedicatedServer_ipv6": "",
|
||||
"__meta_ovhcloud_dedicatedServer_linkSpeed": "123",
|
||||
"__meta_ovhcloud_dedicatedServer_name": "abcde",
|
||||
"__meta_ovhcloud_dedicatedServer_noIntervention": "false",
|
||||
"__meta_ovhcloud_dedicatedServer_os": "debian11_64",
|
||||
"__meta_ovhcloud_dedicatedServer_rack": "TESTRACK",
|
||||
"__meta_ovhcloud_dedicatedServer_reverse": "abcde-rev",
|
||||
"__meta_ovhcloud_dedicatedServer_serverId": "1234",
|
||||
"__meta_ovhcloud_dedicatedServer_state": "test",
|
||||
"__meta_ovhcloud_dedicatedServer_supportLevel": "pro",
|
||||
"instance": "abcde",
|
||||
},
|
||||
} {
|
||||
t.Run(fmt.Sprintf("item %d", i), func(t *testing.T) {
|
||||
require.Equal(t, lbls, targetGroup.Targets[i])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func MockDedicatedAPI(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get("X-Ovh-Application") != ovhcloudApplicationKeyTest {
|
||||
http.Error(w, "bad application key", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if string(r.URL.Path) == "/dedicated/server" {
|
||||
dedicatedServersList, err := os.ReadFile("testdata/dedicated_server/dedicated_servers.json")
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
_, err = w.Write(dedicatedServersList)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
if string(r.URL.Path) == "/dedicated/server/abcde" {
|
||||
dedicatedServer, err := os.ReadFile("testdata/dedicated_server/dedicated_servers_details.json")
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
_, err = w.Write(dedicatedServer)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
if string(r.URL.Path) == "/dedicated/server/abcde/ips" {
|
||||
dedicatedServerIPs, err := os.ReadFile("testdata/dedicated_server/dedicated_servers_abcde_ips.json")
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
_, err = w.Write(dedicatedServerIPs)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
155
discovery/ovhcloud/ovhcloud.go
Normal file
155
discovery/ovhcloud/ovhcloud.go
Normal file
|
@ -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 ovhcloud
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/ovh/go-ovh/ovh"
|
||||
"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"
|
||||
)
|
||||
|
||||
// metaLabelPrefix is the meta prefix used for all meta labels in this discovery.
|
||||
const metaLabelPrefix = model.MetaLabelPrefix + "ovhcloud_"
|
||||
|
||||
type refresher interface {
|
||||
refresh(context.Context) ([]*targetgroup.Group, error)
|
||||
}
|
||||
|
||||
var DefaultSDConfig = SDConfig{
|
||||
Endpoint: "ovh-eu",
|
||||
RefreshInterval: model.Duration(60 * time.Second),
|
||||
}
|
||||
|
||||
// SDConfig defines the Service Discovery struct used for configuration.
|
||||
type SDConfig struct {
|
||||
Endpoint string `yaml:"endpoint"`
|
||||
ApplicationKey string `yaml:"application_key"`
|
||||
ApplicationSecret config.Secret `yaml:"application_secret"`
|
||||
ConsumerKey config.Secret `yaml:"consumer_key"`
|
||||
RefreshInterval model.Duration `yaml:"refresh_interval"`
|
||||
Service string `yaml:"service"`
|
||||
}
|
||||
|
||||
// Name implements the Discoverer interface.
|
||||
func (c SDConfig) Name() string {
|
||||
return "ovhcloud"
|
||||
}
|
||||
|
||||
// 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.Endpoint == "" {
|
||||
return errors.New("endpoint can not be empty")
|
||||
}
|
||||
if c.ApplicationKey == "" {
|
||||
return errors.New("application key can not be empty")
|
||||
}
|
||||
if c.ApplicationSecret == "" {
|
||||
return errors.New("application secret can not be empty")
|
||||
}
|
||||
if c.ConsumerKey == "" {
|
||||
return errors.New("consumer key can not be empty")
|
||||
}
|
||||
switch c.Service {
|
||||
case "dedicated_server", "vps":
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("unknown service: %v", c.Service)
|
||||
}
|
||||
}
|
||||
|
||||
// CreateClient creates a new ovh client configured with given credentials.
|
||||
func createClient(config *SDConfig) (*ovh.Client, error) {
|
||||
return ovh.NewClient(config.Endpoint, config.ApplicationKey, string(config.ApplicationSecret), string(config.ConsumerKey))
|
||||
}
|
||||
|
||||
// NewDiscoverer new discoverer
|
||||
func (c *SDConfig) NewDiscoverer(options discovery.DiscovererOptions) (discovery.Discoverer, error) {
|
||||
return NewDiscovery(c, options.Logger)
|
||||
}
|
||||
|
||||
func init() {
|
||||
discovery.RegisterConfig(&SDConfig{})
|
||||
}
|
||||
|
||||
// ParseIPList parses ip list as they can have different formats.
|
||||
func parseIPList(ipList []string) ([]netip.Addr, error) {
|
||||
var IPs []netip.Addr
|
||||
for _, ip := range ipList {
|
||||
ipAddr, err := netip.ParseAddr(ip)
|
||||
if err != nil {
|
||||
ipPrefix, err := netip.ParsePrefix(ip)
|
||||
if err != nil {
|
||||
return nil, errors.New("could not parse IP addresses from list")
|
||||
}
|
||||
if ipPrefix.IsValid() {
|
||||
netmask := ipPrefix.Bits()
|
||||
if netmask != 32 {
|
||||
continue
|
||||
}
|
||||
ipAddr = ipPrefix.Addr()
|
||||
}
|
||||
}
|
||||
if ipAddr.IsValid() && !ipAddr.IsUnspecified() {
|
||||
IPs = append(IPs, ipAddr)
|
||||
}
|
||||
}
|
||||
|
||||
if len(IPs) < 1 {
|
||||
return nil, errors.New("could not parse IP addresses from list")
|
||||
}
|
||||
return IPs, nil
|
||||
}
|
||||
|
||||
func newRefresher(conf *SDConfig, logger log.Logger) (refresher, error) {
|
||||
switch conf.Service {
|
||||
case "vps":
|
||||
return newVpsDiscovery(conf, logger), nil
|
||||
case "dedicated_server":
|
||||
return newDedicatedServerDiscovery(conf, logger), nil
|
||||
}
|
||||
return nil, fmt.Errorf("unknown OVHcloud discovery service '%s'", conf.Service)
|
||||
}
|
||||
|
||||
// NewDiscovery returns a new Ovhcloud Discoverer which periodically refreshes its targets.
|
||||
func NewDiscovery(conf *SDConfig, logger log.Logger) (*refresh.Discovery, error) {
|
||||
r, err := newRefresher(conf, logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return refresh.NewDiscovery(
|
||||
logger,
|
||||
"ovhcloud",
|
||||
time.Duration(conf.RefreshInterval),
|
||||
r.refresh,
|
||||
), nil
|
||||
}
|
129
discovery/ovhcloud/ovhcloud_test.go
Normal file
129
discovery/ovhcloud/ovhcloud_test.go
Normal file
|
@ -0,0 +1,129 @@
|
|||
// 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 ovhcloud
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus/common/config"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/prometheus/prometheus/discovery"
|
||||
"github.com/prometheus/prometheus/util/testutil"
|
||||
)
|
||||
|
||||
var (
|
||||
ovhcloudApplicationKeyTest = "TDPKJdwZwAQPwKX2"
|
||||
ovhcloudApplicationSecretTest = config.Secret("9ufkBmLaTQ9nz5yMUlg79taH0GNnzDjk")
|
||||
ovhcloudConsumerKeyTest = config.Secret("5mBuy6SUQcRw2ZUxg0cG68BoDKpED4KY")
|
||||
)
|
||||
|
||||
const (
|
||||
mockURL = "https://localhost:1234"
|
||||
)
|
||||
|
||||
func getMockConf(service string) (SDConfig, error) {
|
||||
confString := fmt.Sprintf(`
|
||||
endpoint: %s
|
||||
application_key: %s
|
||||
application_secret: %s
|
||||
consumer_key: %s
|
||||
refresh_interval: 1m
|
||||
service: %s
|
||||
`, mockURL, ovhcloudApplicationKeyTest, ovhcloudApplicationSecretTest, ovhcloudConsumerKeyTest, service)
|
||||
|
||||
return getMockConfFromString(confString)
|
||||
}
|
||||
|
||||
func getMockConfFromString(confString string) (SDConfig, error) {
|
||||
var conf SDConfig
|
||||
err := yaml.UnmarshalStrict([]byte(confString), &conf)
|
||||
return conf, err
|
||||
}
|
||||
|
||||
func TestErrorInitClient(t *testing.T) {
|
||||
confString := fmt.Sprintf(`
|
||||
endpoint: %s
|
||||
|
||||
`, mockURL)
|
||||
|
||||
conf, _ := getMockConfFromString(confString)
|
||||
|
||||
_, err := createClient(&conf)
|
||||
|
||||
require.ErrorContains(t, err, "missing application key")
|
||||
}
|
||||
|
||||
func TestParseIPs(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
input []string
|
||||
want error
|
||||
}{
|
||||
{
|
||||
name: "Parse IPv4 failed.",
|
||||
input: []string{"A.b"},
|
||||
want: errors.New("could not parse IP addresses from list"),
|
||||
},
|
||||
{
|
||||
name: "Parse unspecified failed.",
|
||||
input: []string{"0.0.0.0"},
|
||||
want: errors.New("could not parse IP addresses from list"),
|
||||
},
|
||||
{
|
||||
name: "Parse void IP failed.",
|
||||
input: []string{""},
|
||||
want: errors.New("could not parse IP addresses from list"),
|
||||
},
|
||||
{
|
||||
name: "Parse IPv6 ok.",
|
||||
input: []string{"2001:0db8:0000:0000:0000:0000:0000:0001"},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "Parse IPv6 failed.",
|
||||
input: []string{"bbb:cccc:1111"},
|
||||
want: errors.New("could not parse IP addresses from list"),
|
||||
},
|
||||
{
|
||||
name: "Parse IPv4 bad mask.",
|
||||
input: []string{"192.0.2.1/23"},
|
||||
want: errors.New("could not parse IP addresses from list"),
|
||||
},
|
||||
{
|
||||
name: "Parse IPv4 ok.",
|
||||
input: []string{"192.0.2.1/32"},
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
_, err := parseIPList(tc.input)
|
||||
require.Equal(t, tc.want, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiscoverer(t *testing.T) {
|
||||
conf, _ := getMockConf("vps")
|
||||
logger := testutil.NewLogger(t)
|
||||
_, err := conf.NewDiscoverer(discovery.DiscovererOptions{
|
||||
Logger: logger,
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
}
|
3
discovery/ovhcloud/testdata/dedicated_server/dedicated_servers.json
vendored
Normal file
3
discovery/ovhcloud/testdata/dedicated_server/dedicated_servers.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
[
|
||||
"abcde"
|
||||
]
|
4
discovery/ovhcloud/testdata/dedicated_server/dedicated_servers_abcde_ips.json
vendored
Normal file
4
discovery/ovhcloud/testdata/dedicated_server/dedicated_servers_abcde_ips.json
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
[
|
||||
"1.2.3.4/32",
|
||||
"2001:0db8:0000:0000:0000:0000:0000:0001/64"
|
||||
]
|
20
discovery/ovhcloud/testdata/dedicated_server/dedicated_servers_details.json
vendored
Normal file
20
discovery/ovhcloud/testdata/dedicated_server/dedicated_servers_details.json
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"ip": "1.2.3.4",
|
||||
"newUpgradeSystem": true,
|
||||
"commercialRange": "Advance-1 Gen 2",
|
||||
"rack": "TESTRACK",
|
||||
"rescueMail": null,
|
||||
"supportLevel": "pro",
|
||||
"bootId": 1,
|
||||
"linkSpeed": 123,
|
||||
"professionalUse": false,
|
||||
"monitoring": true,
|
||||
"noIntervention": false,
|
||||
"name": "abcde",
|
||||
"rootDevice": null,
|
||||
"state": "test",
|
||||
"datacenter": "gra3",
|
||||
"os": "debian11_64",
|
||||
"reverse": "abcde-rev",
|
||||
"serverId": 1234
|
||||
}
|
3
discovery/ovhcloud/testdata/vps/vps.json
vendored
Normal file
3
discovery/ovhcloud/testdata/vps/vps.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
[
|
||||
"abc"
|
||||
]
|
4
discovery/ovhcloud/testdata/vps/vps_abc_ips.json
vendored
Normal file
4
discovery/ovhcloud/testdata/vps/vps_abc_ips.json
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
[
|
||||
"192.0.2.1/32",
|
||||
"2001:0db1:0000:0000:0000:0000:0000:0001/64"
|
||||
]
|
25
discovery/ovhcloud/testdata/vps/vps_details.json
vendored
Normal file
25
discovery/ovhcloud/testdata/vps/vps_details.json
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"offerType": "ssd",
|
||||
"monitoringIpBlocks": [],
|
||||
"displayName": "abc",
|
||||
"zone": "zone",
|
||||
"cluster": "cluster_test",
|
||||
"slaMonitoring": false,
|
||||
"name": "abc",
|
||||
"vcore": 1,
|
||||
"state": "running",
|
||||
"keymap": null,
|
||||
"netbootMode": "local",
|
||||
"model": {
|
||||
"name": "vps-value-1-2-40",
|
||||
"availableOptions": [],
|
||||
"maximumAdditionnalIp": 16,
|
||||
"offer": "VPS abc",
|
||||
"disk": 40,
|
||||
"version": "2019v1",
|
||||
"vcore": 1,
|
||||
"memory": 2048,
|
||||
"datacenter": []
|
||||
},
|
||||
"memoryLimit": 2048
|
||||
}
|
186
discovery/ovhcloud/vps.go
Normal file
186
discovery/ovhcloud/vps.go
Normal file
|
@ -0,0 +1,186 @@
|
|||
// 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 ovhcloud
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"path"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/ovh/go-ovh/ovh"
|
||||
"github.com/prometheus/common/model"
|
||||
|
||||
"github.com/prometheus/prometheus/discovery/refresh"
|
||||
"github.com/prometheus/prometheus/discovery/targetgroup"
|
||||
)
|
||||
|
||||
const (
|
||||
vpsAPIPath = "/vps"
|
||||
vpsLabelPrefix = metaLabelPrefix + "vps_"
|
||||
)
|
||||
|
||||
// Model struct from API.
|
||||
type vpsModel struct {
|
||||
MaximumAdditionalIP int `json:"maximumAdditionnalIp"`
|
||||
Offer string `json:"offer"`
|
||||
Datacenter []string `json:"datacenter"`
|
||||
Vcore int `json:"vcore"`
|
||||
Version string `json:"version"`
|
||||
Name string `json:"name"`
|
||||
Disk int `json:"disk"`
|
||||
Memory int `json:"memory"`
|
||||
}
|
||||
|
||||
// VPS struct from API.
|
||||
type virtualPrivateServer struct {
|
||||
ips []netip.Addr
|
||||
Keymap []string `json:"keymap"`
|
||||
Zone string `json:"zone"`
|
||||
Model vpsModel `json:"model"`
|
||||
DisplayName string `json:"displayName"`
|
||||
MonitoringIPBlocks []string `json:"monitoringIpBlocks"`
|
||||
Cluster string `json:"cluster"`
|
||||
State string `json:"state"`
|
||||
Name string `json:"name"`
|
||||
NetbootMode string `json:"netbootMode"`
|
||||
MemoryLimit int `json:"memoryLimit"`
|
||||
OfferType string `json:"offerType"`
|
||||
Vcore int `json:"vcore"`
|
||||
}
|
||||
|
||||
type vpsDiscovery struct {
|
||||
*refresh.Discovery
|
||||
config *SDConfig
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
func newVpsDiscovery(conf *SDConfig, logger log.Logger) *vpsDiscovery {
|
||||
return &vpsDiscovery{config: conf, logger: logger}
|
||||
}
|
||||
|
||||
func getVpsDetails(client *ovh.Client, vpsName string) (*virtualPrivateServer, error) {
|
||||
var vpsDetails virtualPrivateServer
|
||||
vpsNamePath := path.Join(vpsAPIPath, url.QueryEscape(vpsName))
|
||||
|
||||
err := client.Get(vpsNamePath, &vpsDetails)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ips []string
|
||||
err = client.Get(path.Join(vpsNamePath, "ips"), &ips)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parsedIPs, err := parseIPList(ips)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vpsDetails.ips = parsedIPs
|
||||
|
||||
return &vpsDetails, nil
|
||||
}
|
||||
|
||||
func getVpsList(client *ovh.Client) ([]string, error) {
|
||||
var vpsListName []string
|
||||
|
||||
err := client.Get(vpsAPIPath, &vpsListName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return vpsListName, nil
|
||||
}
|
||||
|
||||
func (d *vpsDiscovery) getService() string {
|
||||
return "vps"
|
||||
}
|
||||
|
||||
func (d *vpsDiscovery) getSource() string {
|
||||
return fmt.Sprintf("%s_%s", d.config.Name(), d.getService())
|
||||
}
|
||||
|
||||
func (d *vpsDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) {
|
||||
client, err := createClient(d.config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var vpsDetailedList []virtualPrivateServer
|
||||
vpsList, err := getVpsList(client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, vpsName := range vpsList {
|
||||
vpsDetailed, err := getVpsDetails(client, vpsName)
|
||||
if err != nil {
|
||||
err := level.Warn(d.logger).Log("msg", fmt.Sprintf("%s: Could not get details of %s", d.getSource(), vpsName), "err", err.Error())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
vpsDetailedList = append(vpsDetailedList, *vpsDetailed)
|
||||
}
|
||||
|
||||
var targets []model.LabelSet
|
||||
for _, server := range vpsDetailedList {
|
||||
var ipv4, ipv6 string
|
||||
for _, ip := range server.ips {
|
||||
if ip.Is4() {
|
||||
ipv4 = ip.String()
|
||||
}
|
||||
if ip.Is6() {
|
||||
ipv6 = ip.String()
|
||||
}
|
||||
}
|
||||
defaultIP := ipv4
|
||||
if defaultIP == "" {
|
||||
defaultIP = ipv6
|
||||
}
|
||||
labels := model.LabelSet{
|
||||
model.AddressLabel: model.LabelValue(defaultIP),
|
||||
model.InstanceLabel: model.LabelValue(server.Name),
|
||||
vpsLabelPrefix + "offer": model.LabelValue(server.Model.Offer),
|
||||
vpsLabelPrefix + "datacenter": model.LabelValue(fmt.Sprintf("%+v", server.Model.Datacenter)),
|
||||
vpsLabelPrefix + "model_vcore": model.LabelValue(fmt.Sprintf("%d", server.Model.Vcore)),
|
||||
vpsLabelPrefix + "maximumAdditionalIp": model.LabelValue(fmt.Sprintf("%d", server.Model.MaximumAdditionalIP)),
|
||||
vpsLabelPrefix + "version": model.LabelValue(server.Model.Version),
|
||||
vpsLabelPrefix + "model_name": model.LabelValue(server.Model.Name),
|
||||
vpsLabelPrefix + "disk": model.LabelValue(fmt.Sprintf("%d", server.Model.Disk)),
|
||||
vpsLabelPrefix + "memory": model.LabelValue(fmt.Sprintf("%d", server.Model.Memory)),
|
||||
vpsLabelPrefix + "zone": model.LabelValue(server.Zone),
|
||||
vpsLabelPrefix + "displayName": model.LabelValue(server.DisplayName),
|
||||
vpsLabelPrefix + "cluster": model.LabelValue(server.Cluster),
|
||||
vpsLabelPrefix + "state": model.LabelValue(server.State),
|
||||
vpsLabelPrefix + "name": model.LabelValue(server.Name),
|
||||
vpsLabelPrefix + "netbootMode": model.LabelValue(server.NetbootMode),
|
||||
vpsLabelPrefix + "memoryLimit": model.LabelValue(fmt.Sprintf("%d", server.MemoryLimit)),
|
||||
vpsLabelPrefix + "offerType": model.LabelValue(server.OfferType),
|
||||
vpsLabelPrefix + "vcore": model.LabelValue(fmt.Sprintf("%d", server.Vcore)),
|
||||
vpsLabelPrefix + "ipv4": model.LabelValue(ipv4),
|
||||
vpsLabelPrefix + "ipv6": model.LabelValue(ipv6),
|
||||
}
|
||||
|
||||
targets = append(targets, labels)
|
||||
}
|
||||
|
||||
return []*targetgroup.Group{{Source: d.getSource(), Targets: targets}}, nil
|
||||
}
|
130
discovery/ovhcloud/vps_test.go
Normal file
130
discovery/ovhcloud/vps_test.go
Normal file
|
@ -0,0 +1,130 @@
|
|||
// 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 ovhcloud
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestOvhCloudVpsRefresh(t *testing.T) {
|
||||
var cfg SDConfig
|
||||
|
||||
mock := httptest.NewServer(http.HandlerFunc(MockVpsAPI))
|
||||
defer mock.Close()
|
||||
cfgString := fmt.Sprintf(`
|
||||
---
|
||||
service: vps
|
||||
endpoint: %s
|
||||
application_key: %s
|
||||
application_secret: %s
|
||||
consumer_key: %s`, mock.URL, ovhcloudApplicationKeyTest, ovhcloudApplicationSecretTest, ovhcloudConsumerKeyTest)
|
||||
|
||||
require.NoError(t, yaml.UnmarshalStrict([]byte(cfgString), &cfg))
|
||||
|
||||
d, err := newRefresher(&cfg, log.NewNopLogger())
|
||||
require.NoError(t, err)
|
||||
ctx := context.Background()
|
||||
targetGroups, err := d.refresh(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 1, len(targetGroups))
|
||||
targetGroup := targetGroups[0]
|
||||
require.NotNil(t, targetGroup)
|
||||
require.NotNil(t, targetGroup.Targets)
|
||||
require.Equal(t, 1, len(targetGroup.Targets))
|
||||
for i, lbls := range []model.LabelSet{
|
||||
{
|
||||
"__address__": "192.0.2.1",
|
||||
"__meta_ovhcloud_vps_ipv4": "192.0.2.1",
|
||||
"__meta_ovhcloud_vps_ipv6": "",
|
||||
"__meta_ovhcloud_vps_cluster": "cluster_test",
|
||||
"__meta_ovhcloud_vps_datacenter": "[]",
|
||||
"__meta_ovhcloud_vps_disk": "40",
|
||||
"__meta_ovhcloud_vps_displayName": "abc",
|
||||
"__meta_ovhcloud_vps_maximumAdditionalIp": "16",
|
||||
"__meta_ovhcloud_vps_memory": "2048",
|
||||
"__meta_ovhcloud_vps_memoryLimit": "2048",
|
||||
"__meta_ovhcloud_vps_model_name": "vps-value-1-2-40",
|
||||
"__meta_ovhcloud_vps_name": "abc",
|
||||
"__meta_ovhcloud_vps_netbootMode": "local",
|
||||
"__meta_ovhcloud_vps_offer": "VPS abc",
|
||||
"__meta_ovhcloud_vps_offerType": "ssd",
|
||||
"__meta_ovhcloud_vps_state": "running",
|
||||
"__meta_ovhcloud_vps_vcore": "1",
|
||||
"__meta_ovhcloud_vps_model_vcore": "1",
|
||||
"__meta_ovhcloud_vps_version": "2019v1",
|
||||
"__meta_ovhcloud_vps_zone": "zone",
|
||||
"instance": "abc",
|
||||
},
|
||||
} {
|
||||
t.Run(fmt.Sprintf("item %d", i), func(t *testing.T) {
|
||||
require.Equal(t, lbls, targetGroup.Targets[i])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func MockVpsAPI(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get("X-Ovh-Application") != ovhcloudApplicationKeyTest {
|
||||
http.Error(w, "bad application key", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if string(r.URL.Path) == "/vps" {
|
||||
dedicatedServersList, err := os.ReadFile("testdata/vps/vps.json")
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
_, err = w.Write(dedicatedServersList)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
if string(r.URL.Path) == "/vps/abc" {
|
||||
dedicatedServer, err := os.ReadFile("testdata/vps/vps_details.json")
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
_, err = w.Write(dedicatedServer)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
if string(r.URL.Path) == "/vps/abc/ips" {
|
||||
dedicatedServerIPs, err := os.ReadFile("testdata/vps/vps_abc_ips.json")
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
_, err = w.Write(dedicatedServerIPs)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
|
@ -294,6 +294,10 @@ nomad_sd_configs:
|
|||
openstack_sd_configs:
|
||||
[ - <openstack_sd_config> ... ]
|
||||
|
||||
# List of OVHcloud service discovery configurations.
|
||||
ovhcloud_sd_configs:
|
||||
[ - <ovhcloud_sd_config> ... ]
|
||||
|
||||
# List of PuppetDB service discovery configurations.
|
||||
puppetdb_sd_configs:
|
||||
[ - <puppetdb_sd_config> ... ]
|
||||
|
@ -1176,6 +1180,68 @@ tls_config:
|
|||
[ <tls_config> ]
|
||||
```
|
||||
|
||||
### `<ovhcloud_sd_config>`
|
||||
|
||||
OVHcloud SD configurations allow retrieving scrape targets from OVHcloud's [dedicated servers](https://www.ovhcloud.com/en/bare-metal/) and [VPS](https://www.ovhcloud.com/en/vps/) using
|
||||
their [API](https://api.ovh.com/).
|
||||
Prometheus will periodically check the REST endpoint and create a target for every discovered server.
|
||||
The role will try to use the public IPv4 address as default address, if there's none it will try to use the IPv6 one. This may be changed with relabeling.
|
||||
For OVHcloud's [public cloud instances](https://www.ovhcloud.com/en/public-cloud/) you can use the [openstack_sd_config](#openstack_sd_config).
|
||||
|
||||
#### VPS
|
||||
|
||||
* `__meta_ovhcloud_vps_ipv4`: the ipv4 of the server
|
||||
* `__meta_ovhcloud_vps_ipv6`: the ipv6 of the server
|
||||
* `__meta_ovhcloud_vps_keymap`: the KVM keyboard layout on VPS Cloud
|
||||
* `__meta_ovhcloud_vps_zone`: the zone of the server
|
||||
* `__meta_ovhcloud_vps_maximumAdditionalIp`: the maximumAdditionalIp of the server
|
||||
* `__meta_ovhcloud_vps_offer`: the offer of the server
|
||||
* `__meta_ovhcloud_vps_datacenter`: the datacenter of the server
|
||||
* `__meta_ovhcloud_vps_vcore`: the vcore of the server
|
||||
* `__meta_ovhcloud_vps_version`: the version of the server
|
||||
* `__meta_ovhcloud_vps_name`: the name of the server
|
||||
* `__meta_ovhcloud_vps_disk`: the disk of the server
|
||||
* `__meta_ovhcloud_vps_memory`: the memory of the server
|
||||
* `__meta_ovhcloud_vps_displayName`: the name displayed in ManagerV6 for your VPS
|
||||
* `__meta_ovhcloud_vps_monitoringIpBlocks`: the Ip blocks for OVH monitoring servers
|
||||
* `__meta_ovhcloud_vps_cluster`: the cluster of the server
|
||||
* `__meta_ovhcloud_vps_state`: the state of the server
|
||||
* `__meta_ovhcloud_vps_name`: the name of the server
|
||||
* `__meta_ovhcloud_vps_netbootMode`: the netbootMode of the server
|
||||
* `__meta_ovhcloud_vps_memoryLimit`: the memoryLimit of the server
|
||||
* `__meta_ovhcloud_vps_offerType`: the offerType of the server
|
||||
* `__meta_ovhcloud_vps_vcore`: the vcore of the server
|
||||
|
||||
#### Dedicated servers
|
||||
|
||||
* `__meta_ovhcloud_dedicated_server_state`: the state of the server
|
||||
* `__meta_ovhcloud_dedicated_server_ipv4`: the ipv4 of the server
|
||||
* `__meta_ovhcloud_dedicated_server_ipv6`: the ipv6 of the server
|
||||
* `__meta_ovhcloud_dedicated_server_commercialRange`: the dedicated server commercial range
|
||||
* `__meta_ovhcloud_dedicated_server_linkSpeed`: the linkSpeed of the server
|
||||
* `__meta_ovhcloud_dedicated_server_rack`: the rack of the server
|
||||
* `__meta_ovhcloud_dedicated_server_os`: operating system
|
||||
* `__meta_ovhcloud_dedicated_server_supportLevel`: the supportLevel of the server
|
||||
* `__meta_ovhcloud_dedicated_server_serverId`: your server id
|
||||
* `__meta_ovhcloud_dedicated_server_reverse`: dedicated server reverse
|
||||
* `__meta_ovhcloud_dedicated_server_datacenter`: the dedicated datacenter localisation
|
||||
* `__meta_ovhcloud_dedicated_server_name`: the dedicated server name
|
||||
|
||||
See below for the configuration options for OVHcloud discovery:
|
||||
|
||||
```yaml
|
||||
# Access key to use. https://api.ovh.com
|
||||
application_key: <string>
|
||||
application_secret: <secret>
|
||||
consumer_key: <secret>
|
||||
# Service of the targets to retrieve. Must be `vps` or `dedicated_server`.
|
||||
service: <string>
|
||||
# API endpoint. https://github.com/ovh/go-ovh#supported-apis
|
||||
[ endpoint: <string> | default = "ovh-eu" ]
|
||||
# Refresh interval to re-read the resources list.
|
||||
[ refresh_interval: <duration> | default = 60s ]
|
||||
```
|
||||
|
||||
### `<puppetdb_sd_config>`
|
||||
|
||||
PuppetDB SD configurations allow retrieving scrape targets from
|
||||
|
@ -2965,6 +3031,10 @@ nomad_sd_configs:
|
|||
openstack_sd_configs:
|
||||
[ - <openstack_sd_config> ... ]
|
||||
|
||||
# List of OVHcloud service discovery configurations.
|
||||
ovhcloud_sd_configs:
|
||||
[ - <ovhcloud_sd_config> ... ]
|
||||
|
||||
# List of PuppetDB service discovery configurations.
|
||||
puppetdb_sd_configs:
|
||||
[ - <puppetdb_sd_config> ... ]
|
||||
|
|
16
documentation/examples/prometheus-ovhcloud.yml
Normal file
16
documentation/examples/prometheus-ovhcloud.yml
Normal file
|
@ -0,0 +1,16 @@
|
|||
# An example scrape configuration for running Prometheus with Ovhcloud.
|
||||
scrape_configs:
|
||||
- job_name: 'ovhcloud'
|
||||
ovhcloud_sd_configs:
|
||||
- service: vps
|
||||
endpoint: ovh-eu
|
||||
application_key: XXX
|
||||
application_secret: XXX
|
||||
consumer_key: XXX
|
||||
refresh_interval: 1m
|
||||
- service: dedicated_server
|
||||
endpoint: ovh-eu
|
||||
application_key: XXX
|
||||
application_secret: XXX
|
||||
consumer_key: XXX
|
||||
refresh_interval: 1m
|
1
go.mod
1
go.mod
|
@ -38,6 +38,7 @@ require (
|
|||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f
|
||||
github.com/oklog/run v1.1.0
|
||||
github.com/oklog/ulid v1.3.1
|
||||
github.com/ovh/go-ovh v1.1.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/alertmanager v0.24.0
|
||||
github.com/prometheus/client_golang v1.13.0
|
||||
|
|
3
go.sum
3
go.sum
|
@ -677,6 +677,8 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxS
|
|||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/ovh/go-ovh v1.1.0 h1:bHXZmw8nTgZin4Nv7JuaLs0KG5x54EQR7migYTd1zrk=
|
||||
github.com/ovh/go-ovh v1.1.0/go.mod h1:AxitLZ5HBRPyUd+Zl60Ajaag+rNTdVXWIkzfrVuTXWA=
|
||||
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
|
@ -1445,6 +1447,7 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
|
|||
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.66.6 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI=
|
||||
gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
- github.com/prometheus/prometheus/discovery/moby
|
||||
- github.com/prometheus/prometheus/discovery/nomad
|
||||
- github.com/prometheus/prometheus/discovery/openstack
|
||||
- github.com/prometheus/prometheus/discovery/ovhcloud
|
||||
- github.com/prometheus/prometheus/discovery/puppetdb
|
||||
- github.com/prometheus/prometheus/discovery/scaleway
|
||||
- github.com/prometheus/prometheus/discovery/triton
|
||||
|
|
|
@ -61,6 +61,9 @@ import (
|
|||
// Register openstack plugin.
|
||||
_ "github.com/prometheus/prometheus/discovery/openstack"
|
||||
|
||||
// Register ovhcloud plugin.
|
||||
_ "github.com/prometheus/prometheus/discovery/ovhcloud"
|
||||
|
||||
// Register puppetdb plugin.
|
||||
_ "github.com/prometheus/prometheus/discovery/puppetdb"
|
||||
|
||||
|
|
Loading…
Reference in a new issue