mirror of
https://github.com/prometheus/prometheus.git
synced 2024-11-09 23:24: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/moby"
|
||||||
"github.com/prometheus/prometheus/discovery/nomad"
|
"github.com/prometheus/prometheus/discovery/nomad"
|
||||||
"github.com/prometheus/prometheus/discovery/openstack"
|
"github.com/prometheus/prometheus/discovery/openstack"
|
||||||
|
"github.com/prometheus/prometheus/discovery/ovhcloud"
|
||||||
"github.com/prometheus/prometheus/discovery/puppetdb"
|
"github.com/prometheus/prometheus/discovery/puppetdb"
|
||||||
"github.com/prometheus/prometheus/discovery/scaleway"
|
"github.com/prometheus/prometheus/discovery/scaleway"
|
||||||
"github.com/prometheus/prometheus/discovery/targetgroup"
|
"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",
|
JobName: "scaleway",
|
||||||
|
|
||||||
|
@ -1175,7 +1205,7 @@ func TestElideSecrets(t *testing.T) {
|
||||||
yamlConfig := string(config)
|
yamlConfig := string(config)
|
||||||
|
|
||||||
matches := secretRe.FindAllStringIndex(yamlConfig, -1)
|
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",
|
require.NotContains(t, yamlConfig, "mysecret",
|
||||||
"yaml marshal reveals authentication credentials.")
|
"yaml marshal reveals authentication credentials.")
|
||||||
}
|
}
|
||||||
|
@ -1618,6 +1648,14 @@ var expectedErrors = []struct {
|
||||||
filename: "ionos_datacenter.bad.yml",
|
filename: "ionos_datacenter.bad.yml",
|
||||||
errMsg: "datacenter id can't be empty",
|
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) {
|
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:
|
eureka_sd_configs:
|
||||||
- server: "http://eureka.example.com:8761/eureka"
|
- 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
|
- job_name: scaleway
|
||||||
scaleway_sd_configs:
|
scaleway_sd_configs:
|
||||||
- role: instance
|
- 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/moby" // register moby
|
||||||
_ "github.com/prometheus/prometheus/discovery/nomad" // register nomad
|
_ "github.com/prometheus/prometheus/discovery/nomad" // register nomad
|
||||||
_ "github.com/prometheus/prometheus/discovery/openstack" // register openstack
|
_ "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/puppetdb" // register puppetdb
|
||||||
_ "github.com/prometheus/prometheus/discovery/scaleway" // register scaleway
|
_ "github.com/prometheus/prometheus/discovery/scaleway" // register scaleway
|
||||||
_ "github.com/prometheus/prometheus/discovery/triton" // register triton
|
_ "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_configs:
|
||||||
[ - <openstack_sd_config> ... ]
|
[ - <openstack_sd_config> ... ]
|
||||||
|
|
||||||
|
# List of OVHcloud service discovery configurations.
|
||||||
|
ovhcloud_sd_configs:
|
||||||
|
[ - <ovhcloud_sd_config> ... ]
|
||||||
|
|
||||||
# List of PuppetDB service discovery configurations.
|
# List of PuppetDB service discovery configurations.
|
||||||
puppetdb_sd_configs:
|
puppetdb_sd_configs:
|
||||||
[ - <puppetdb_sd_config> ... ]
|
[ - <puppetdb_sd_config> ... ]
|
||||||
|
@ -1176,6 +1180,68 @@ tls_config:
|
||||||
[ <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_config>`
|
||||||
|
|
||||||
PuppetDB SD configurations allow retrieving scrape targets from
|
PuppetDB SD configurations allow retrieving scrape targets from
|
||||||
|
@ -2965,6 +3031,10 @@ nomad_sd_configs:
|
||||||
openstack_sd_configs:
|
openstack_sd_configs:
|
||||||
[ - <openstack_sd_config> ... ]
|
[ - <openstack_sd_config> ... ]
|
||||||
|
|
||||||
|
# List of OVHcloud service discovery configurations.
|
||||||
|
ovhcloud_sd_configs:
|
||||||
|
[ - <ovhcloud_sd_config> ... ]
|
||||||
|
|
||||||
# List of PuppetDB service discovery configurations.
|
# List of PuppetDB service discovery configurations.
|
||||||
puppetdb_sd_configs:
|
puppetdb_sd_configs:
|
||||||
[ - <puppetdb_sd_config> ... ]
|
[ - <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/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f
|
||||||
github.com/oklog/run v1.1.0
|
github.com/oklog/run v1.1.0
|
||||||
github.com/oklog/ulid v1.3.1
|
github.com/oklog/ulid v1.3.1
|
||||||
|
github.com/ovh/go-ovh v1.1.0
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/prometheus/alertmanager v0.24.0
|
github.com/prometheus/alertmanager v0.24.0
|
||||||
github.com/prometheus/client_golang v1.13.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.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.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||||
github.com/openzipkin/zipkin-go v0.2.2/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/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.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
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/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 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
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 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI=
|
||||||
gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
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/moby
|
||||||
- github.com/prometheus/prometheus/discovery/nomad
|
- github.com/prometheus/prometheus/discovery/nomad
|
||||||
- github.com/prometheus/prometheus/discovery/openstack
|
- github.com/prometheus/prometheus/discovery/openstack
|
||||||
|
- github.com/prometheus/prometheus/discovery/ovhcloud
|
||||||
- github.com/prometheus/prometheus/discovery/puppetdb
|
- github.com/prometheus/prometheus/discovery/puppetdb
|
||||||
- github.com/prometheus/prometheus/discovery/scaleway
|
- github.com/prometheus/prometheus/discovery/scaleway
|
||||||
- github.com/prometheus/prometheus/discovery/triton
|
- github.com/prometheus/prometheus/discovery/triton
|
||||||
|
|
|
@ -61,6 +61,9 @@ import (
|
||||||
// Register openstack plugin.
|
// Register openstack plugin.
|
||||||
_ "github.com/prometheus/prometheus/discovery/openstack"
|
_ "github.com/prometheus/prometheus/discovery/openstack"
|
||||||
|
|
||||||
|
// Register ovhcloud plugin.
|
||||||
|
_ "github.com/prometheus/prometheus/discovery/ovhcloud"
|
||||||
|
|
||||||
// Register puppetdb plugin.
|
// Register puppetdb plugin.
|
||||||
_ "github.com/prometheus/prometheus/discovery/puppetdb"
|
_ "github.com/prometheus/prometheus/discovery/puppetdb"
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue