mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-24 05:04:05 -08:00
Add Eureka Service Discovery (#3369)
Signed-off-by: kangwoo <kangwoo@gmail.com>
This commit is contained in:
parent
cfd4e05c9e
commit
7c0d5ae4e7
|
@ -34,6 +34,7 @@ import (
|
||||||
"github.com/prometheus/prometheus/discovery/dns"
|
"github.com/prometheus/prometheus/discovery/dns"
|
||||||
"github.com/prometheus/prometheus/discovery/dockerswarm"
|
"github.com/prometheus/prometheus/discovery/dockerswarm"
|
||||||
"github.com/prometheus/prometheus/discovery/ec2"
|
"github.com/prometheus/prometheus/discovery/ec2"
|
||||||
|
"github.com/prometheus/prometheus/discovery/eureka"
|
||||||
"github.com/prometheus/prometheus/discovery/file"
|
"github.com/prometheus/prometheus/discovery/file"
|
||||||
"github.com/prometheus/prometheus/discovery/hetzner"
|
"github.com/prometheus/prometheus/discovery/hetzner"
|
||||||
"github.com/prometheus/prometheus/discovery/kubernetes"
|
"github.com/prometheus/prometheus/discovery/kubernetes"
|
||||||
|
@ -677,6 +678,22 @@ var expectedConf = &Config{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
JobName: "service-eureka",
|
||||||
|
|
||||||
|
HonorTimestamps: true,
|
||||||
|
ScrapeInterval: model.Duration(15 * time.Second),
|
||||||
|
ScrapeTimeout: DefaultGlobalConfig.ScrapeTimeout,
|
||||||
|
|
||||||
|
MetricsPath: DefaultScrapeConfig.MetricsPath,
|
||||||
|
Scheme: DefaultScrapeConfig.Scheme,
|
||||||
|
|
||||||
|
ServiceDiscoveryConfigs: discovery.Configs{&eureka.SDConfig{
|
||||||
|
Server: "http://eureka.example.com:8761/eureka",
|
||||||
|
RefreshInterval: model.Duration(30 * time.Second),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
AlertingConfig: AlertingConfig{
|
AlertingConfig: AlertingConfig{
|
||||||
AlertmanagerConfigs: []*AlertmanagerConfig{
|
AlertmanagerConfigs: []*AlertmanagerConfig{
|
||||||
|
@ -996,6 +1013,14 @@ var expectedErrors = []struct {
|
||||||
filename: "hetzner_role.bad.yml",
|
filename: "hetzner_role.bad.yml",
|
||||||
errMsg: "unknown role",
|
errMsg: "unknown role",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
filename: "eureka_no_server.bad.yml",
|
||||||
|
errMsg: "empty or null eureka server",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filename: "eureka_invalid_server.bad.yml",
|
||||||
|
errMsg: "invalid eureka server URL",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBadConfigs(t *testing.T) {
|
func TestBadConfigs(t *testing.T) {
|
||||||
|
|
4
config/testdata/conf.good.yml
vendored
4
config/testdata/conf.good.yml
vendored
|
@ -288,6 +288,10 @@ scrape_configs:
|
||||||
username: abcdef
|
username: abcdef
|
||||||
password: abcdef
|
password: abcdef
|
||||||
|
|
||||||
|
- job_name: service-eureka
|
||||||
|
eureka_sd_configs:
|
||||||
|
- server: 'http://eureka.example.com:8761/eureka'
|
||||||
|
|
||||||
alerting:
|
alerting:
|
||||||
alertmanagers:
|
alertmanagers:
|
||||||
- scheme: https
|
- scheme: https
|
||||||
|
|
5
config/testdata/eureka_invalid_server.bad.yml
vendored
Normal file
5
config/testdata/eureka_invalid_server.bad.yml
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
scrape_configs:
|
||||||
|
|
||||||
|
- job_name: eureka
|
||||||
|
eureka_sd_configs:
|
||||||
|
- server: eureka.com
|
5
config/testdata/eureka_no_server.bad.yml
vendored
Normal file
5
config/testdata/eureka_no_server.bad.yml
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
scrape_configs:
|
||||||
|
|
||||||
|
- job_name: eureka
|
||||||
|
eureka_sd_configs:
|
||||||
|
- server:
|
110
discovery/eureka/client.go
Normal file
110
discovery/eureka/client.go
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
// Copyright 2020 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 eureka
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Applications struct {
|
||||||
|
VersionsDelta int `xml:"versions__delta"`
|
||||||
|
AppsHashcode string `xml:"apps__hashcode"`
|
||||||
|
Applications []Application `xml:"application"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Application struct {
|
||||||
|
Name string `xml:"name"`
|
||||||
|
Instances []Instance `xml:"instance"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Port struct {
|
||||||
|
Port int `xml:",chardata"`
|
||||||
|
Enabled bool `xml:"enabled,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Instance struct {
|
||||||
|
HostName string `xml:"hostName"`
|
||||||
|
HomePageURL string `xml:"homePageUrl"`
|
||||||
|
StatusPageURL string `xml:"statusPageUrl"`
|
||||||
|
HealthCheckURL string `xml:"healthCheckUrl"`
|
||||||
|
App string `xml:"app"`
|
||||||
|
IPAddr string `xml:"ipAddr"`
|
||||||
|
VipAddress string `xml:"vipAddress"`
|
||||||
|
SecureVipAddress string `xml:"secureVipAddress"`
|
||||||
|
Status string `xml:"status"`
|
||||||
|
Port *Port `xml:"port"`
|
||||||
|
SecurePort *Port `xml:"securePort"`
|
||||||
|
DataCenterInfo *DataCenterInfo `xml:"dataCenterInfo"`
|
||||||
|
Metadata *MetaData `xml:"metadata"`
|
||||||
|
IsCoordinatingDiscoveryServer bool `xml:"isCoordinatingDiscoveryServer"`
|
||||||
|
LastUpdatedTimestamp int `xml:"lastUpdatedTimestamp"`
|
||||||
|
LastDirtyTimestamp int `xml:"lastDirtyTimestamp"`
|
||||||
|
ActionType string `xml:"actionType"`
|
||||||
|
CountryID int `xml:"countryId"`
|
||||||
|
InstanceID string `xml:"instanceId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MetaData struct {
|
||||||
|
Items []Tag `xml:",any"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tag struct {
|
||||||
|
XMLName xml.Name
|
||||||
|
Content string `xml:",innerxml"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DataCenterInfo struct {
|
||||||
|
Name string `xml:"name"`
|
||||||
|
Class string `xml:"class,attr"`
|
||||||
|
Metadata *MetaData `xml:"metadata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const appListPath string = "/apps"
|
||||||
|
|
||||||
|
func fetchApps(ctx context.Context, server string, client *http.Client) (*Applications, error) {
|
||||||
|
url := fmt.Sprintf("%s%s", server, appListPath)
|
||||||
|
|
||||||
|
request, err := http.NewRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
request = request.WithContext(ctx)
|
||||||
|
|
||||||
|
resp, err := client.Do(request)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
io.Copy(ioutil.Discard, resp.Body)
|
||||||
|
resp.Body.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
if resp.StatusCode/100 != 2 {
|
||||||
|
return nil, errors.Errorf("non 2xx status '%d' response during eureka service discovery", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
var apps Applications
|
||||||
|
err = xml.NewDecoder(resp.Body).Decode(&apps)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "%q", url)
|
||||||
|
}
|
||||||
|
return &apps, nil
|
||||||
|
}
|
213
discovery/eureka/client_test.go
Normal file
213
discovery/eureka/client_test.go
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
// Copyright 2020 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 eureka
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/prometheus/prometheus/util/testutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFetchApps(t *testing.T) {
|
||||||
|
appsXML := `<applications>
|
||||||
|
<versions__delta>1</versions__delta>
|
||||||
|
<apps__hashcode>UP_4_</apps__hashcode>
|
||||||
|
<application>
|
||||||
|
<name>CONFIG-SERVICE</name>
|
||||||
|
<instance>
|
||||||
|
<instanceId>config-service001.test.com:config-service:8080</instanceId>
|
||||||
|
<hostName>config-service001.test.com</hostName>
|
||||||
|
<app>CONFIG-SERVICE</app>
|
||||||
|
<ipAddr>192.133.83.31</ipAddr>
|
||||||
|
<status>UP</status>
|
||||||
|
<overriddenstatus>UNKNOWN</overriddenstatus>
|
||||||
|
<port enabled="true">8080</port>
|
||||||
|
<securePort enabled="false">8080</securePort>
|
||||||
|
<countryId>1</countryId>
|
||||||
|
<dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
|
||||||
|
<name>MyOwn</name>
|
||||||
|
</dataCenterInfo>
|
||||||
|
<leaseInfo>
|
||||||
|
<renewalIntervalInSecs>30</renewalIntervalInSecs>
|
||||||
|
<durationInSecs>90</durationInSecs>
|
||||||
|
<registrationTimestamp>1596003469304</registrationTimestamp>
|
||||||
|
<lastRenewalTimestamp>1596110179310</lastRenewalTimestamp>
|
||||||
|
<evictionTimestamp>0</evictionTimestamp>
|
||||||
|
<serviceUpTimestamp>1547190033103</serviceUpTimestamp>
|
||||||
|
</leaseInfo>
|
||||||
|
<metadata>
|
||||||
|
<instanceId>config-service001.test.com:config-service:8080</instanceId>
|
||||||
|
</metadata>
|
||||||
|
<homePageUrl>http://config-service001.test.com:8080/</homePageUrl>
|
||||||
|
<statusPageUrl>http://config-service001.test.com:8080/info</statusPageUrl>
|
||||||
|
<healthCheckUrl>http://config-service001.test.com 8080/health</healthCheckUrl>
|
||||||
|
<vipAddress>config-service</vipAddress>
|
||||||
|
<isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
|
||||||
|
<lastUpdatedTimestamp>1596003469304</lastUpdatedTimestamp>
|
||||||
|
<lastDirtyTimestamp>1596003469304</lastDirtyTimestamp>
|
||||||
|
<actionType>ADDED</actionType>
|
||||||
|
</instance>
|
||||||
|
<instance>
|
||||||
|
<instanceId>config-service002.test.com:config-service:8080</instanceId>
|
||||||
|
<hostName>config-service002.test.com</hostName>
|
||||||
|
<app>CONFIG-SERVICE</app>
|
||||||
|
<ipAddr>192.133.83.31</ipAddr>
|
||||||
|
<status>UP</status>
|
||||||
|
<overriddenstatus>UNKNOWN</overriddenstatus>
|
||||||
|
<port enabled="true">8080</port>
|
||||||
|
<securePort enabled="false">8080</securePort>
|
||||||
|
<countryId>1</countryId>
|
||||||
|
<dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
|
||||||
|
<name>MyOwn</name>
|
||||||
|
</dataCenterInfo>
|
||||||
|
<leaseInfo>
|
||||||
|
<renewalIntervalInSecs>30</renewalIntervalInSecs>
|
||||||
|
<durationInSecs>90</durationInSecs>
|
||||||
|
<registrationTimestamp>1596003469304</registrationTimestamp>
|
||||||
|
<lastRenewalTimestamp>1596110179310</lastRenewalTimestamp>
|
||||||
|
<evictionTimestamp>0</evictionTimestamp>
|
||||||
|
<serviceUpTimestamp>1547190033103</serviceUpTimestamp>
|
||||||
|
</leaseInfo>
|
||||||
|
<metadata>
|
||||||
|
<instanceId>config-service002.test.com:config-service:8080</instanceId>
|
||||||
|
</metadata>
|
||||||
|
<homePageUrl>http://config-service002.test.com:8080/</homePageUrl>
|
||||||
|
<statusPageUrl>http://config-service002.test.com:8080/info</statusPageUrl>
|
||||||
|
<healthCheckUrl>http://config-service002.test.com:8080/health</healthCheckUrl>
|
||||||
|
<vipAddress>config-service</vipAddress>
|
||||||
|
<isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
|
||||||
|
<lastUpdatedTimestamp>1596003469304</lastUpdatedTimestamp>
|
||||||
|
<lastDirtyTimestamp>1596003469304</lastDirtyTimestamp>
|
||||||
|
<actionType>ADDED</actionType>
|
||||||
|
</instance>
|
||||||
|
</application>
|
||||||
|
<application>
|
||||||
|
<name>META-SERVICE</name>
|
||||||
|
<instance>
|
||||||
|
<instanceId>meta-service002.test.com:meta-service:8080</instanceId>
|
||||||
|
<hostName>meta-service002.test.com</hostName>
|
||||||
|
<app>META-SERVICE</app>
|
||||||
|
<ipAddr>192.133.87.237</ipAddr>
|
||||||
|
<status>UP</status>
|
||||||
|
<overriddenstatus>UNKNOWN</overriddenstatus>
|
||||||
|
<port enabled="true">8080</port>
|
||||||
|
<securePort enabled="false">443</securePort>
|
||||||
|
<countryId>1</countryId>
|
||||||
|
<dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
|
||||||
|
<name>MyOwn</name>
|
||||||
|
</dataCenterInfo>
|
||||||
|
<leaseInfo>
|
||||||
|
<renewalIntervalInSecs>30</renewalIntervalInSecs>
|
||||||
|
<durationInSecs>90</durationInSecs>
|
||||||
|
<registrationTimestamp>1535444352472</registrationTimestamp>
|
||||||
|
<lastRenewalTimestamp>1596110168846</lastRenewalTimestamp>
|
||||||
|
<evictionTimestamp>0</evictionTimestamp>
|
||||||
|
<serviceUpTimestamp>1535444352472</serviceUpTimestamp>
|
||||||
|
</leaseInfo>
|
||||||
|
<metadata>
|
||||||
|
<project>meta-service</project>
|
||||||
|
<management.port>8090</management.port>
|
||||||
|
</metadata>
|
||||||
|
<homePageUrl>http://meta-service002.test.com:8080/</homePageUrl>
|
||||||
|
<statusPageUrl>http://meta-service002.test.com:8080/info</statusPageUrl>
|
||||||
|
<healthCheckUrl>http://meta-service002.test.com:8080/health</healthCheckUrl>
|
||||||
|
<vipAddress>meta-service</vipAddress>
|
||||||
|
<secureVipAddress>meta-service</secureVipAddress>
|
||||||
|
<isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
|
||||||
|
<lastUpdatedTimestamp>1535444352472</lastUpdatedTimestamp>
|
||||||
|
<lastDirtyTimestamp>1535444352398</lastDirtyTimestamp>
|
||||||
|
<actionType>ADDED</actionType>
|
||||||
|
</instance>
|
||||||
|
<instance>
|
||||||
|
<instanceId>meta-service001.test.com:meta-service:8080</instanceId>
|
||||||
|
<hostName>meta-service001.test.com</hostName>
|
||||||
|
<app>META-SERVICE</app>
|
||||||
|
<ipAddr>192.133.87.236</ipAddr>
|
||||||
|
<status>UP</status>
|
||||||
|
<overriddenstatus>UNKNOWN</overriddenstatus>
|
||||||
|
<port enabled="true">8080</port>
|
||||||
|
<securePort enabled="false">443</securePort>
|
||||||
|
<countryId>1</countryId>
|
||||||
|
<dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
|
||||||
|
<name>MyOwn</name>
|
||||||
|
</dataCenterInfo>
|
||||||
|
<leaseInfo>
|
||||||
|
<renewalIntervalInSecs>30</renewalIntervalInSecs>
|
||||||
|
<durationInSecs>90</durationInSecs>
|
||||||
|
<registrationTimestamp>1535444352472</registrationTimestamp>
|
||||||
|
<lastRenewalTimestamp>1596110168846</lastRenewalTimestamp>
|
||||||
|
<evictionTimestamp>0</evictionTimestamp>
|
||||||
|
<serviceUpTimestamp>1535444352472</serviceUpTimestamp>
|
||||||
|
</leaseInfo>
|
||||||
|
<metadata>
|
||||||
|
<project>meta-service</project>
|
||||||
|
<management.port>8090</management.port>
|
||||||
|
</metadata>
|
||||||
|
<homePageUrl>http://meta-service001.test.com:8080/</homePageUrl>
|
||||||
|
<statusPageUrl>http://meta-service001.test.com:8080/info</statusPageUrl>
|
||||||
|
<healthCheckUrl>http://meta-service001.test.com:8080/health</healthCheckUrl>
|
||||||
|
<vipAddress>meta-service</vipAddress>
|
||||||
|
<secureVipAddress>meta-service</secureVipAddress>
|
||||||
|
<isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
|
||||||
|
<lastUpdatedTimestamp>1535444352472</lastUpdatedTimestamp>
|
||||||
|
<lastDirtyTimestamp>1535444352398</lastDirtyTimestamp>
|
||||||
|
<actionType>ADDED</actionType>
|
||||||
|
</instance>
|
||||||
|
</application>
|
||||||
|
</applications>`
|
||||||
|
|
||||||
|
// Simulate apps with a valid XML response.
|
||||||
|
respHandler := func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Header().Set("Content-Type", "application/xml")
|
||||||
|
io.WriteString(w, appsXML)
|
||||||
|
}
|
||||||
|
// Create a test server with mock HTTP handler.
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(respHandler))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
apps, err := fetchApps(context.TODO(), ts.URL, &http.Client{})
|
||||||
|
testutil.Ok(t, err)
|
||||||
|
|
||||||
|
testutil.Equals(t, len(apps.Applications), 2)
|
||||||
|
testutil.Equals(t, apps.Applications[0].Name, "CONFIG-SERVICE")
|
||||||
|
testutil.Equals(t, apps.Applications[1].Name, "META-SERVICE")
|
||||||
|
|
||||||
|
testutil.Equals(t, len(apps.Applications[1].Instances), 2)
|
||||||
|
testutil.Equals(t, apps.Applications[1].Instances[0].InstanceID, "meta-service002.test.com:meta-service:8080")
|
||||||
|
testutil.Equals(t, apps.Applications[1].Instances[0].Metadata.Items[0].XMLName.Local, "project")
|
||||||
|
testutil.Equals(t, apps.Applications[1].Instances[0].Metadata.Items[0].Content, "meta-service")
|
||||||
|
testutil.Equals(t, apps.Applications[1].Instances[0].Metadata.Items[1].XMLName.Local, "management.port")
|
||||||
|
testutil.Equals(t, apps.Applications[1].Instances[0].Metadata.Items[1].Content, "8090")
|
||||||
|
testutil.Equals(t, apps.Applications[1].Instances[1].InstanceID, "meta-service001.test.com:meta-service:8080")
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test500ErrorHttpResponse(t *testing.T) {
|
||||||
|
// Simulate 500 error.
|
||||||
|
respHandler := func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
w.Header().Set("Content-Type", "application/xml")
|
||||||
|
io.WriteString(w, ``)
|
||||||
|
}
|
||||||
|
// Create a test server with mock HTTP handler.
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(respHandler))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
_, err := fetchApps(context.TODO(), ts.URL, &http.Client{})
|
||||||
|
testutil.NotOk(t, err, "5xx HTTP response")
|
||||||
|
}
|
220
discovery/eureka/eureka.go
Normal file
220
discovery/eureka/eureka.go
Normal file
|
@ -0,0 +1,220 @@
|
||||||
|
// Copyright 2020 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 eureka
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-kit/kit/log"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/prometheus/common/config"
|
||||||
|
"github.com/prometheus/common/model"
|
||||||
|
|
||||||
|
"github.com/prometheus/prometheus/discovery"
|
||||||
|
"github.com/prometheus/prometheus/discovery/refresh"
|
||||||
|
"github.com/prometheus/prometheus/discovery/targetgroup"
|
||||||
|
"github.com/prometheus/prometheus/util/strutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// metaLabelPrefix is the meta prefix used for all meta labels.
|
||||||
|
// in this discovery.
|
||||||
|
metaLabelPrefix = model.MetaLabelPrefix + "eureka_"
|
||||||
|
metaAppInstanceLabelPrefix = metaLabelPrefix + "app_instance_"
|
||||||
|
|
||||||
|
appNameLabel = metaLabelPrefix + "app_name"
|
||||||
|
appInstanceHostNameLabel = metaAppInstanceLabelPrefix + "hostname"
|
||||||
|
appInstanceHomePageURLLabel = metaAppInstanceLabelPrefix + "homepage_url"
|
||||||
|
appInstanceStatusPageURLLabel = metaAppInstanceLabelPrefix + "statuspage_url"
|
||||||
|
appInstanceHealthCheckURLLabel = metaAppInstanceLabelPrefix + "healthcheck_url"
|
||||||
|
appInstanceIPAddrLabel = metaAppInstanceLabelPrefix + "ip_addr"
|
||||||
|
appInstanceVipAddressLabel = metaAppInstanceLabelPrefix + "vip_address"
|
||||||
|
appInstanceSecureVipAddressLabel = metaAppInstanceLabelPrefix + "secure_vip_address"
|
||||||
|
appInstanceStatusLabel = metaAppInstanceLabelPrefix + "status"
|
||||||
|
appInstancePortLabel = metaAppInstanceLabelPrefix + "port"
|
||||||
|
appInstancePortEnabledLabel = metaAppInstanceLabelPrefix + "port_enabled"
|
||||||
|
appInstanceSecurePortLabel = metaAppInstanceLabelPrefix + "secure_port"
|
||||||
|
appInstanceSecurePortEnabledLabel = metaAppInstanceLabelPrefix + "secure_port_enabled"
|
||||||
|
appInstanceDataCenterInfoNameLabel = metaAppInstanceLabelPrefix + "datacenterinfo_name"
|
||||||
|
appInstanceDataCenterInfoMetadataPrefix = metaAppInstanceLabelPrefix + "datacenterinfo_metadata_"
|
||||||
|
appInstanceCountryIDLabel = metaAppInstanceLabelPrefix + "country_id"
|
||||||
|
appInstanceIDLabel = metaAppInstanceLabelPrefix + "id"
|
||||||
|
appInstanceMetadataPrefix = metaAppInstanceLabelPrefix + "metadata_"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultSDConfig is the default Eureka SD configuration.
|
||||||
|
var DefaultSDConfig = SDConfig{
|
||||||
|
RefreshInterval: model.Duration(30 * time.Second),
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
discovery.RegisterConfig(&SDConfig{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SDConfig is the configuration for applications running on Eureka.
|
||||||
|
type SDConfig struct {
|
||||||
|
Server string `yaml:"server,omitempty"`
|
||||||
|
HTTPClientConfig config.HTTPClientConfig `yaml:",inline"`
|
||||||
|
RefreshInterval model.Duration `yaml:"refresh_interval,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the name of the Config.
|
||||||
|
func (*SDConfig) Name() string { return "eureka" }
|
||||||
|
|
||||||
|
// NewDiscoverer returns a Discoverer for the Config.
|
||||||
|
func (c *SDConfig) NewDiscoverer(opts discovery.DiscovererOptions) (discovery.Discoverer, error) {
|
||||||
|
return NewDiscovery(c, opts.Logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDirectory joins any relative file paths with dir.
|
||||||
|
func (c *SDConfig) SetDirectory(dir string) {
|
||||||
|
c.HTTPClientConfig.SetDirectory(dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 len(c.Server) == 0 {
|
||||||
|
return errors.New("eureka_sd: empty or null eureka server")
|
||||||
|
}
|
||||||
|
url, err := url.Parse(c.Server)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(url.Scheme) == 0 || len(url.Host) == 0 {
|
||||||
|
return errors.New("eureka_sd: invalid eureka server URL")
|
||||||
|
}
|
||||||
|
return c.HTTPClientConfig.Validate()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discovery provides service discovery based on a Eureka instance.
|
||||||
|
type Discovery struct {
|
||||||
|
*refresh.Discovery
|
||||||
|
client *http.Client
|
||||||
|
server string
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new Eureka discovery for the given role.
|
||||||
|
func NewDiscovery(conf *SDConfig, logger log.Logger) (*Discovery, error) {
|
||||||
|
rt, err := config.NewRoundTripperFromConfig(conf.HTTPClientConfig, "eureka_sd", false, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
d := &Discovery{
|
||||||
|
client: &http.Client{Transport: rt},
|
||||||
|
server: conf.Server,
|
||||||
|
}
|
||||||
|
d.Discovery = refresh.NewDiscovery(
|
||||||
|
logger,
|
||||||
|
"eureka",
|
||||||
|
time.Duration(conf.RefreshInterval),
|
||||||
|
d.refresh,
|
||||||
|
)
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) {
|
||||||
|
apps, err := fetchApps(ctx, d.server, d.client)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tg := &targetgroup.Group{
|
||||||
|
Source: "eureka",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, app := range apps.Applications {
|
||||||
|
targets := targetsForApp(&app)
|
||||||
|
tg.Targets = append(tg.Targets, targets...)
|
||||||
|
}
|
||||||
|
return []*targetgroup.Group{tg}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func targetsForApp(app *Application) []model.LabelSet {
|
||||||
|
targets := make([]model.LabelSet, 0, len(app.Instances))
|
||||||
|
|
||||||
|
// Gather info about the app's 'instances'. Each instance is considered a task.
|
||||||
|
for _, t := range app.Instances {
|
||||||
|
var targetAddress string
|
||||||
|
if t.Port != nil {
|
||||||
|
targetAddress = net.JoinHostPort(t.HostName, strconv.Itoa(t.Port.Port))
|
||||||
|
} else {
|
||||||
|
targetAddress = net.JoinHostPort(t.HostName, "80")
|
||||||
|
}
|
||||||
|
|
||||||
|
target := model.LabelSet{
|
||||||
|
model.AddressLabel: lv(targetAddress),
|
||||||
|
model.InstanceLabel: lv(t.InstanceID),
|
||||||
|
|
||||||
|
appNameLabel: lv(app.Name),
|
||||||
|
appInstanceHostNameLabel: lv(t.HostName),
|
||||||
|
appInstanceHomePageURLLabel: lv(t.HomePageURL),
|
||||||
|
appInstanceStatusPageURLLabel: lv(t.StatusPageURL),
|
||||||
|
appInstanceHealthCheckURLLabel: lv(t.HealthCheckURL),
|
||||||
|
appInstanceIPAddrLabel: lv(t.IPAddr),
|
||||||
|
appInstanceVipAddressLabel: lv(t.VipAddress),
|
||||||
|
appInstanceSecureVipAddressLabel: lv(t.SecureVipAddress),
|
||||||
|
appInstanceStatusLabel: lv(t.Status),
|
||||||
|
appInstanceCountryIDLabel: lv(strconv.Itoa(t.CountryID)),
|
||||||
|
appInstanceIDLabel: lv(t.InstanceID),
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.Port != nil {
|
||||||
|
target[appInstancePortLabel] = lv(strconv.Itoa(t.Port.Port))
|
||||||
|
target[appInstancePortEnabledLabel] = lv(strconv.FormatBool(t.Port.Enabled))
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.SecurePort != nil {
|
||||||
|
target[appInstanceSecurePortLabel] = lv(strconv.Itoa(t.SecurePort.Port))
|
||||||
|
target[appInstanceSecurePortEnabledLabel] = lv(strconv.FormatBool(t.SecurePort.Enabled))
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.DataCenterInfo != nil {
|
||||||
|
target[appInstanceDataCenterInfoNameLabel] = lv(t.DataCenterInfo.Name)
|
||||||
|
|
||||||
|
if t.DataCenterInfo.Metadata != nil {
|
||||||
|
for _, m := range t.DataCenterInfo.Metadata.Items {
|
||||||
|
ln := strutil.SanitizeLabelName(m.XMLName.Local)
|
||||||
|
target[model.LabelName(appInstanceDataCenterInfoMetadataPrefix+ln)] = lv(m.Content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.Metadata != nil {
|
||||||
|
for _, m := range t.Metadata.Items {
|
||||||
|
ln := strutil.SanitizeLabelName(m.XMLName.Local)
|
||||||
|
target[model.LabelName(appInstanceMetadataPrefix+ln)] = lv(m.Content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
targets = append(targets, target)
|
||||||
|
|
||||||
|
}
|
||||||
|
return targets
|
||||||
|
}
|
||||||
|
|
||||||
|
func lv(s string) model.LabelValue {
|
||||||
|
return model.LabelValue(s)
|
||||||
|
}
|
246
discovery/eureka/eureka_test.go
Normal file
246
discovery/eureka/eureka_test.go
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
// Copyright 2020 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 eureka
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/prometheus/prometheus/util/testutil"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/prometheus/common/model"
|
||||||
|
"github.com/prometheus/prometheus/discovery/targetgroup"
|
||||||
|
)
|
||||||
|
|
||||||
|
func testUpdateServices(respHandler http.HandlerFunc) ([]*targetgroup.Group, error) {
|
||||||
|
// Create a test server with mock HTTP handler.
|
||||||
|
ts := httptest.NewServer(respHandler)
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
conf := SDConfig{
|
||||||
|
Server: ts.URL,
|
||||||
|
}
|
||||||
|
|
||||||
|
md, err := NewDiscovery(&conf, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return md.refresh(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEurekaSDHandleError(t *testing.T) {
|
||||||
|
var (
|
||||||
|
errTesting = errors.Errorf("non 2xx status '%d' response during eureka service discovery", http.StatusInternalServerError)
|
||||||
|
respHandler = func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
w.Header().Set("Content-Type", "application/xml")
|
||||||
|
io.WriteString(w, ``)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
tgs, err := testUpdateServices(respHandler)
|
||||||
|
|
||||||
|
testutil.ErrorEqual(t, err, errTesting)
|
||||||
|
testutil.Equals(t, len(tgs), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEurekaSDEmptyList(t *testing.T) {
|
||||||
|
var (
|
||||||
|
appsXML = `<applications>
|
||||||
|
<versions__delta>1</versions__delta>
|
||||||
|
<apps__hashcode/>
|
||||||
|
</applications>`
|
||||||
|
respHandler = func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Header().Set("Content-Type", "application/xml")
|
||||||
|
io.WriteString(w, appsXML)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
tgs, err := testUpdateServices(respHandler)
|
||||||
|
testutil.Ok(t, err)
|
||||||
|
testutil.Equals(t, len(tgs), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEurekaSDSendGroup(t *testing.T) {
|
||||||
|
var (
|
||||||
|
appsXML = `<applications>
|
||||||
|
<versions__delta>1</versions__delta>
|
||||||
|
<apps__hashcode>UP_4_</apps__hashcode>
|
||||||
|
<application>
|
||||||
|
<name>CONFIG-SERVICE</name>
|
||||||
|
<instance>
|
||||||
|
<instanceId>config-service001.test.com:config-service:8080</instanceId>
|
||||||
|
<hostName>config-service001.test.com</hostName>
|
||||||
|
<app>CONFIG-SERVICE</app>
|
||||||
|
<ipAddr>192.133.83.31</ipAddr>
|
||||||
|
<status>UP</status>
|
||||||
|
<overriddenstatus>UNKNOWN</overriddenstatus>
|
||||||
|
<port enabled="true">8080</port>
|
||||||
|
<securePort enabled="false">8080</securePort>
|
||||||
|
<countryId>1</countryId>
|
||||||
|
<dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
|
||||||
|
<name>MyOwn</name>
|
||||||
|
</dataCenterInfo>
|
||||||
|
<leaseInfo>
|
||||||
|
<renewalIntervalInSecs>30</renewalIntervalInSecs>
|
||||||
|
<durationInSecs>90</durationInSecs>
|
||||||
|
<registrationTimestamp>1596003469304</registrationTimestamp>
|
||||||
|
<lastRenewalTimestamp>1596110179310</lastRenewalTimestamp>
|
||||||
|
<evictionTimestamp>0</evictionTimestamp>
|
||||||
|
<serviceUpTimestamp>1547190033103</serviceUpTimestamp>
|
||||||
|
</leaseInfo>
|
||||||
|
<metadata>
|
||||||
|
<instanceId>config-service001.test.com:config-service:8080</instanceId>
|
||||||
|
</metadata>
|
||||||
|
<homePageUrl>http://config-service001.test.com:8080/</homePageUrl>
|
||||||
|
<statusPageUrl>http://config-service001.test.com:8080/info</statusPageUrl>
|
||||||
|
<healthCheckUrl>http://config-service001.test.com 8080/health</healthCheckUrl>
|
||||||
|
<vipAddress>config-service</vipAddress>
|
||||||
|
<isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
|
||||||
|
<lastUpdatedTimestamp>1596003469304</lastUpdatedTimestamp>
|
||||||
|
<lastDirtyTimestamp>1596003469304</lastDirtyTimestamp>
|
||||||
|
<actionType>ADDED</actionType>
|
||||||
|
</instance>
|
||||||
|
<instance>
|
||||||
|
<instanceId>config-service002.test.com:config-service:8080</instanceId>
|
||||||
|
<hostName>config-service002.test.com</hostName>
|
||||||
|
<app>CONFIG-SERVICE</app>
|
||||||
|
<ipAddr>192.133.83.31</ipAddr>
|
||||||
|
<status>UP</status>
|
||||||
|
<overriddenstatus>UNKNOWN</overriddenstatus>
|
||||||
|
<port enabled="true">8080</port>
|
||||||
|
<securePort enabled="false">8080</securePort>
|
||||||
|
<countryId>1</countryId>
|
||||||
|
<dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
|
||||||
|
<name>MyOwn</name>
|
||||||
|
</dataCenterInfo>
|
||||||
|
<leaseInfo>
|
||||||
|
<renewalIntervalInSecs>30</renewalIntervalInSecs>
|
||||||
|
<durationInSecs>90</durationInSecs>
|
||||||
|
<registrationTimestamp>1596003469304</registrationTimestamp>
|
||||||
|
<lastRenewalTimestamp>1596110179310</lastRenewalTimestamp>
|
||||||
|
<evictionTimestamp>0</evictionTimestamp>
|
||||||
|
<serviceUpTimestamp>1547190033103</serviceUpTimestamp>
|
||||||
|
</leaseInfo>
|
||||||
|
<metadata>
|
||||||
|
<instanceId>config-service002.test.com:config-service:8080</instanceId>
|
||||||
|
</metadata>
|
||||||
|
<homePageUrl>http://config-service002.test.com:8080/</homePageUrl>
|
||||||
|
<statusPageUrl>http://config-service002.test.com:8080/info</statusPageUrl>
|
||||||
|
<healthCheckUrl>http://config-service002.test.com:8080/health</healthCheckUrl>
|
||||||
|
<vipAddress>config-service</vipAddress>
|
||||||
|
<isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
|
||||||
|
<lastUpdatedTimestamp>1596003469304</lastUpdatedTimestamp>
|
||||||
|
<lastDirtyTimestamp>1596003469304</lastDirtyTimestamp>
|
||||||
|
<actionType>ADDED</actionType>
|
||||||
|
</instance>
|
||||||
|
</application>
|
||||||
|
<application>
|
||||||
|
<name>META-SERVICE</name>
|
||||||
|
<instance>
|
||||||
|
<instanceId>meta-service002.test.com:meta-service:8080</instanceId>
|
||||||
|
<hostName>meta-service002.test.com</hostName>
|
||||||
|
<app>META-SERVICE</app>
|
||||||
|
<ipAddr>192.133.87.237</ipAddr>
|
||||||
|
<status>UP</status>
|
||||||
|
<overriddenstatus>UNKNOWN</overriddenstatus>
|
||||||
|
<port enabled="true">8080</port>
|
||||||
|
<securePort enabled="false">443</securePort>
|
||||||
|
<countryId>1</countryId>
|
||||||
|
<dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
|
||||||
|
<name>MyOwn</name>
|
||||||
|
</dataCenterInfo>
|
||||||
|
<leaseInfo>
|
||||||
|
<renewalIntervalInSecs>30</renewalIntervalInSecs>
|
||||||
|
<durationInSecs>90</durationInSecs>
|
||||||
|
<registrationTimestamp>1535444352472</registrationTimestamp>
|
||||||
|
<lastRenewalTimestamp>1596110168846</lastRenewalTimestamp>
|
||||||
|
<evictionTimestamp>0</evictionTimestamp>
|
||||||
|
<serviceUpTimestamp>1535444352472</serviceUpTimestamp>
|
||||||
|
</leaseInfo>
|
||||||
|
<metadata>
|
||||||
|
<project>meta-service</project>
|
||||||
|
<management.port>8090</management.port>
|
||||||
|
</metadata>
|
||||||
|
<homePageUrl>http://meta-service002.test.com:8080/</homePageUrl>
|
||||||
|
<statusPageUrl>http://meta-service002.test.com:8080/info</statusPageUrl>
|
||||||
|
<healthCheckUrl>http://meta-service002.test.com:8080/health</healthCheckUrl>
|
||||||
|
<vipAddress>meta-service</vipAddress>
|
||||||
|
<secureVipAddress>meta-service</secureVipAddress>
|
||||||
|
<isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
|
||||||
|
<lastUpdatedTimestamp>1535444352472</lastUpdatedTimestamp>
|
||||||
|
<lastDirtyTimestamp>1535444352398</lastDirtyTimestamp>
|
||||||
|
<actionType>ADDED</actionType>
|
||||||
|
</instance>
|
||||||
|
<instance>
|
||||||
|
<instanceId>meta-service001.test.com:meta-service:8080</instanceId>
|
||||||
|
<hostName>meta-service001.test.com</hostName>
|
||||||
|
<app>META-SERVICE</app>
|
||||||
|
<ipAddr>192.133.87.236</ipAddr>
|
||||||
|
<status>UP</status>
|
||||||
|
<overriddenstatus>UNKNOWN</overriddenstatus>
|
||||||
|
<port enabled="true">8080</port>
|
||||||
|
<securePort enabled="false">443</securePort>
|
||||||
|
<countryId>1</countryId>
|
||||||
|
<dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
|
||||||
|
<name>MyOwn</name>
|
||||||
|
</dataCenterInfo>
|
||||||
|
<leaseInfo>
|
||||||
|
<renewalIntervalInSecs>30</renewalIntervalInSecs>
|
||||||
|
<durationInSecs>90</durationInSecs>
|
||||||
|
<registrationTimestamp>1535444352472</registrationTimestamp>
|
||||||
|
<lastRenewalTimestamp>1596110168846</lastRenewalTimestamp>
|
||||||
|
<evictionTimestamp>0</evictionTimestamp>
|
||||||
|
<serviceUpTimestamp>1535444352472</serviceUpTimestamp>
|
||||||
|
</leaseInfo>
|
||||||
|
<metadata>
|
||||||
|
<project>meta-service</project>
|
||||||
|
<management.port>8090</management.port>
|
||||||
|
</metadata>
|
||||||
|
<homePageUrl>http://meta-service001.test.com:8080/</homePageUrl>
|
||||||
|
<statusPageUrl>http://meta-service001.test.com:8080/info</statusPageUrl>
|
||||||
|
<healthCheckUrl>http://meta-service001.test.com:8080/health</healthCheckUrl>
|
||||||
|
<vipAddress>meta-service</vipAddress>
|
||||||
|
<secureVipAddress>meta-service</secureVipAddress>
|
||||||
|
<isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
|
||||||
|
<lastUpdatedTimestamp>1535444352472</lastUpdatedTimestamp>
|
||||||
|
<lastDirtyTimestamp>1535444352398</lastDirtyTimestamp>
|
||||||
|
<actionType>ADDED</actionType>
|
||||||
|
</instance>
|
||||||
|
</application>
|
||||||
|
</applications>`
|
||||||
|
respHandler = func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Header().Set("Content-Type", "application/xml")
|
||||||
|
io.WriteString(w, appsXML)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
tgs, err := testUpdateServices(respHandler)
|
||||||
|
testutil.Ok(t, err)
|
||||||
|
testutil.Equals(t, len(tgs), 1)
|
||||||
|
|
||||||
|
tg := tgs[0]
|
||||||
|
testutil.Equals(t, tg.Source, "eureka")
|
||||||
|
testutil.Equals(t, len(tg.Targets), 4)
|
||||||
|
|
||||||
|
tgt := tg.Targets[0]
|
||||||
|
testutil.Equals(t, tgt[model.AddressLabel], model.LabelValue("config-service001.test.com:8080"))
|
||||||
|
|
||||||
|
tgt = tg.Targets[2]
|
||||||
|
testutil.Equals(t, tgt[model.AddressLabel], model.LabelValue("meta-service002.test.com:8080"))
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ import (
|
||||||
_ "github.com/prometheus/prometheus/discovery/dns" // register dns
|
_ "github.com/prometheus/prometheus/discovery/dns" // register dns
|
||||||
_ "github.com/prometheus/prometheus/discovery/dockerswarm" // register dockerswarm
|
_ "github.com/prometheus/prometheus/discovery/dockerswarm" // register dockerswarm
|
||||||
_ "github.com/prometheus/prometheus/discovery/ec2" // register ec2
|
_ "github.com/prometheus/prometheus/discovery/ec2" // register ec2
|
||||||
|
_ "github.com/prometheus/prometheus/discovery/eureka" // register eureka
|
||||||
_ "github.com/prometheus/prometheus/discovery/file" // register file
|
_ "github.com/prometheus/prometheus/discovery/file" // register file
|
||||||
_ "github.com/prometheus/prometheus/discovery/gce" // register gce
|
_ "github.com/prometheus/prometheus/discovery/gce" // register gce
|
||||||
_ "github.com/prometheus/prometheus/discovery/hetzner" // register hetzner
|
_ "github.com/prometheus/prometheus/discovery/hetzner" // register hetzner
|
||||||
|
|
|
@ -207,6 +207,10 @@ dns_sd_configs:
|
||||||
ec2_sd_configs:
|
ec2_sd_configs:
|
||||||
[ - <ec2_sd_config> ... ]
|
[ - <ec2_sd_config> ... ]
|
||||||
|
|
||||||
|
# List of Eureka service discovery configurations.
|
||||||
|
eureka_sd_configs:
|
||||||
|
[ - <eureka_sd_config> ... ]
|
||||||
|
|
||||||
# List of file service discovery configurations.
|
# List of file service discovery configurations.
|
||||||
file_sd_configs:
|
file_sd_configs:
|
||||||
[ - <file_sd_config> ... ]
|
[ - <file_sd_config> ... ]
|
||||||
|
@ -1381,6 +1385,72 @@ tls_config:
|
||||||
[ <tls_config> ]
|
[ <tls_config> ]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `<eureka_sd_config>`
|
||||||
|
|
||||||
|
Eureka SD configurations allow retrieving scrape targets using the
|
||||||
|
[Eureka](https://github.com/Netflix/eureka) REST API. Prometheus
|
||||||
|
will periodically check the REST endpoint and
|
||||||
|
create a target for every app instance.
|
||||||
|
|
||||||
|
The following meta labels are available on targets during [relabeling](#relabel_config):
|
||||||
|
|
||||||
|
* `__meta_eureka_app_name`: the name of the app
|
||||||
|
* `__meta_eureka_app_instance_id`: the ID of the app instance
|
||||||
|
* `__meta_eureka_app_instance_hostname`: the hostname of the instance
|
||||||
|
* `__meta_eureka_app_instance_homepage_url`: the homepage url of the app instance
|
||||||
|
* `__meta_eureka_app_instance_statuspage_url`: the status page url of the app instance
|
||||||
|
* `__meta_eureka_app_instance_healthcheck_url`: the health check url of the app instance
|
||||||
|
* `__meta_eureka_app_instance_ip_addr`: the IP address of the app instance
|
||||||
|
* `__meta_eureka_app_instance_vip_address`: the VIP address of the app instance
|
||||||
|
* `__meta_eureka_app_instance_secure_vip_address`: the secure VIP address of the app instance
|
||||||
|
* `__meta_eureka_app_instance_status`: the status of the app instance
|
||||||
|
* `__meta_eureka_app_instance_port`: the port of the app instance
|
||||||
|
* `__meta_eureka_app_instance_port_enabled`: the port enabled of the app instance
|
||||||
|
* `__meta_eureka_app_instance_secure_port`: the secure port address of the app instance
|
||||||
|
* `__meta_eureka_app_instance_secure_port_enabled`: the secure port of the app instance
|
||||||
|
* `__meta_eureka_app_instance_country_id`: the country ID of the app instance
|
||||||
|
* `__meta_eureka_app_instance_metadata_<metadataname>`: app instance metadata
|
||||||
|
* `__meta_eureka_app_instance_datacenterinfo_name`: the datacenter name of the app instance
|
||||||
|
* `__meta_eureka_app_instance_datacenterinfo_<metadataname>`: the datacenter metadata
|
||||||
|
|
||||||
|
See below for the configuration options for Eureka discovery:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# The URL to connect to the Eureka server.
|
||||||
|
server: <string>
|
||||||
|
|
||||||
|
# Sets the `Authorization` header on every request with the
|
||||||
|
# configured username and password.
|
||||||
|
# password and password_file are mutually exclusive.
|
||||||
|
basic_auth:
|
||||||
|
[ username: <string> ]
|
||||||
|
[ password: <secret> ]
|
||||||
|
[ password_file: <string> ]
|
||||||
|
|
||||||
|
# Sets the `Authorization` header on every request with
|
||||||
|
# the configured bearer token. It is mutually exclusive with `bearer_token_file`.
|
||||||
|
[ bearer_token: <string> ]
|
||||||
|
|
||||||
|
# Sets the `Authorization` header on every request with the bearer token
|
||||||
|
# read from the configured file. It is mutually exclusive with `bearer_token`.
|
||||||
|
[ bearer_token_file: <filename> ]
|
||||||
|
|
||||||
|
# Configures the scrape request's TLS settings.
|
||||||
|
tls_config:
|
||||||
|
[ <tls_config> ]
|
||||||
|
|
||||||
|
# Optional proxy URL.
|
||||||
|
[ proxy_url: <string> ]
|
||||||
|
|
||||||
|
# Refresh interval to re-read the app instance list.
|
||||||
|
[ refresh_interval: <duration> | default = 30s ]
|
||||||
|
```
|
||||||
|
|
||||||
|
See [the Prometheus eureka-sd configuration file](/documentation/examples/prometheus-eureka.yml)
|
||||||
|
for a practical example on how to set up your Eureka app and your Prometheus
|
||||||
|
configuration.
|
||||||
|
|
||||||
|
|
||||||
### `<static_config>`
|
### `<static_config>`
|
||||||
|
|
||||||
A `static_config` allows specifying a list of targets and a common label set
|
A `static_config` allows specifying a list of targets and a common label set
|
||||||
|
@ -1557,6 +1627,10 @@ dns_sd_configs:
|
||||||
ec2_sd_configs:
|
ec2_sd_configs:
|
||||||
[ - <ec2_sd_config> ... ]
|
[ - <ec2_sd_config> ... ]
|
||||||
|
|
||||||
|
# List of Eureka service discovery configurations.
|
||||||
|
eureka_sd_configs:
|
||||||
|
[ - <eureka_sd_config> ... ]
|
||||||
|
|
||||||
# List of file service discovery configurations.
|
# List of file service discovery configurations.
|
||||||
file_sd_configs:
|
file_sd_configs:
|
||||||
[ - <file_sd_config> ... ]
|
[ - <file_sd_config> ... ]
|
||||||
|
|
66
documentation/examples/prometheus-eureka.yml
Normal file
66
documentation/examples/prometheus-eureka.yml
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
# A example scrape configuration for running Prometheus with Eureka.
|
||||||
|
|
||||||
|
scrape_configs:
|
||||||
|
|
||||||
|
# Make Prometheus scrape itself for metrics.
|
||||||
|
- job_name: 'prometheus'
|
||||||
|
static_configs:
|
||||||
|
- targets: ['localhost:9090']
|
||||||
|
|
||||||
|
# Discover Eureka services to scrape.
|
||||||
|
- job_name: 'eureka'
|
||||||
|
|
||||||
|
# Scrape Eureka itself to discover new services.
|
||||||
|
eureka_sd_configs:
|
||||||
|
- server: http://localhost:8761/eureka
|
||||||
|
|
||||||
|
relabel_configs:
|
||||||
|
# You can use Eureka's application instance metadata.
|
||||||
|
# If you are using SpringBoot, you can add metadata using eureka.instance.metadataMap like this:
|
||||||
|
# application.yaml (spring-boot)
|
||||||
|
# eureka:
|
||||||
|
# instance:
|
||||||
|
# metadataMap:
|
||||||
|
# "prometheus.scrape": "true"
|
||||||
|
# "prometheus.path": "/actuator/prometheus"
|
||||||
|
# "prometheus.port": "8080"
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Example relabel to scrape only application that have
|
||||||
|
# "prometheus.scrape = true" metadata.
|
||||||
|
# - source_labels: [__meta_eureka_app_instance_metadata_prometheus_scrape]
|
||||||
|
# action: keep
|
||||||
|
# regex: true
|
||||||
|
#
|
||||||
|
# application.yaml (spring-boot)
|
||||||
|
# eureka:
|
||||||
|
# instance:
|
||||||
|
# metadataMap:
|
||||||
|
# "prometheus.scrape": "true"
|
||||||
|
#
|
||||||
|
# Example relabel to customize metric path based on application
|
||||||
|
# "prometheus.path = <metric path>" annotation.
|
||||||
|
# - source_labels: [__meta_eureka_app_instance_metadata_prometheus_path]
|
||||||
|
# action: replace
|
||||||
|
# target_label: __metrics_path__
|
||||||
|
# regex: (.+)
|
||||||
|
#
|
||||||
|
# application.yaml (spring-boot)
|
||||||
|
# eureka:
|
||||||
|
# instance:
|
||||||
|
# metadataMap:
|
||||||
|
# "prometheus.path": "/actuator/prometheus"
|
||||||
|
#
|
||||||
|
# Example relabel to scrape only single, desired port for the application
|
||||||
|
# based on application "prometheus.port = <port>" metadata.
|
||||||
|
# - source_labels: [__address__, __meta_eureka_app_instance_metadata_prometheus_port]
|
||||||
|
# action: replace
|
||||||
|
# regex: ([^:]+)(?::\d+)?;(\d+)
|
||||||
|
# replacement: $1:$2
|
||||||
|
# target_label: __address__
|
||||||
|
#
|
||||||
|
# application.yaml (spring-boot)
|
||||||
|
# eureka:
|
||||||
|
# instance:
|
||||||
|
# metadataMap:
|
||||||
|
# "prometheus.port": "8080"
|
Loading…
Reference in a new issue