mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-26 14:09:41 -08:00
Merge branch 'master' into dev-2.0
This commit is contained in:
commit
c389193b37
|
@ -49,6 +49,12 @@ Debian packages [are available](https://packages.debian.org/sid/net/prometheus).
|
||||||
|
|
||||||
Docker images are available on [Quay.io](https://quay.io/repository/prometheus/prometheus).
|
Docker images are available on [Quay.io](https://quay.io/repository/prometheus/prometheus).
|
||||||
|
|
||||||
|
You can launch a Prometheus container for trying it out with
|
||||||
|
|
||||||
|
$ docker run --name prometheus -d -p 127.0.0.1:9090:9090 quay.io/prometheus/prometheus
|
||||||
|
|
||||||
|
Prometheus will now be reachable at http://localhost:9090/.
|
||||||
|
|
||||||
### Building from source
|
### Building from source
|
||||||
|
|
||||||
To build Prometheus from the source code yourself you need to have a working
|
To build Prometheus from the source code yourself you need to have a working
|
||||||
|
|
|
@ -241,6 +241,7 @@ func resolveFilepaths(baseDir string, cfg *Config) {
|
||||||
kcfg.TLSConfig.KeyFile = join(kcfg.TLSConfig.KeyFile)
|
kcfg.TLSConfig.KeyFile = join(kcfg.TLSConfig.KeyFile)
|
||||||
}
|
}
|
||||||
for _, mcfg := range cfg.MarathonSDConfigs {
|
for _, mcfg := range cfg.MarathonSDConfigs {
|
||||||
|
mcfg.BearerTokenFile = join(mcfg.BearerTokenFile)
|
||||||
mcfg.TLSConfig.CAFile = join(mcfg.TLSConfig.CAFile)
|
mcfg.TLSConfig.CAFile = join(mcfg.TLSConfig.CAFile)
|
||||||
mcfg.TLSConfig.CertFile = join(mcfg.TLSConfig.CertFile)
|
mcfg.TLSConfig.CertFile = join(mcfg.TLSConfig.CertFile)
|
||||||
mcfg.TLSConfig.KeyFile = join(mcfg.TLSConfig.KeyFile)
|
mcfg.TLSConfig.KeyFile = join(mcfg.TLSConfig.KeyFile)
|
||||||
|
@ -920,6 +921,8 @@ type MarathonSDConfig struct {
|
||||||
Timeout model.Duration `yaml:"timeout,omitempty"`
|
Timeout model.Duration `yaml:"timeout,omitempty"`
|
||||||
RefreshInterval model.Duration `yaml:"refresh_interval,omitempty"`
|
RefreshInterval model.Duration `yaml:"refresh_interval,omitempty"`
|
||||||
TLSConfig TLSConfig `yaml:"tls_config,omitempty"`
|
TLSConfig TLSConfig `yaml:"tls_config,omitempty"`
|
||||||
|
BearerToken string `yaml:"bearer_token,omitempty"`
|
||||||
|
BearerTokenFile string `yaml:"bearer_token_file,omitempty"`
|
||||||
|
|
||||||
// Catches all undefined fields and must be empty after parsing.
|
// Catches all undefined fields and must be empty after parsing.
|
||||||
XXX map[string]interface{} `yaml:",inline"`
|
XXX map[string]interface{} `yaml:",inline"`
|
||||||
|
@ -939,6 +942,9 @@ func (c *MarathonSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) erro
|
||||||
if len(c.Servers) == 0 {
|
if len(c.Servers) == 0 {
|
||||||
return fmt.Errorf("Marathon SD config must contain at least one Marathon server")
|
return fmt.Errorf("Marathon SD config must contain at least one Marathon server")
|
||||||
}
|
}
|
||||||
|
if len(c.BearerToken) > 0 && len(c.BearerTokenFile) > 0 {
|
||||||
|
return fmt.Errorf("at most one of bearer_token & bearer_token_file must be configured")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
{{/* vim: set ft=html: */}}
|
{{/* vim: set ft=html: */}}
|
||||||
{{/* Load Prometheus console library JS/CSS. Should go in <head> */}}
|
{{/* Load Prometheus console library JS/CSS. Should go in <head> */}}
|
||||||
{{ define "prom_console_head" }}
|
{{ define "prom_console_head" }}
|
||||||
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/static/vendor/rickshaw/rickshaw.min.css">
|
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/static/vendor/rickshaw/rickshaw.min.css?v={{ buildVersion }}">
|
||||||
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/static/vendor/bootstrap-3.3.1/css/bootstrap.min.css">
|
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/static/vendor/bootstrap-3.3.1/css/bootstrap.min.css?v={{ buildVersion }}">
|
||||||
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/static/css/prom_console.css">
|
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/static/css/prom_console.css?v={{ buildVersion }}">
|
||||||
<script src="{{ pathPrefix }}/static/vendor/rickshaw/vendor/d3.v3.js"></script>
|
<script src="{{ pathPrefix }}/static/vendor/rickshaw/vendor/d3.v3.js?v={{ buildVersion }}"></script>
|
||||||
<script src="{{ pathPrefix }}/static/vendor/rickshaw/vendor/d3.layout.min.js"></script>
|
<script src="{{ pathPrefix }}/static/vendor/rickshaw/vendor/d3.layout.min.js?v={{ buildVersion }}"></script>
|
||||||
<script src="{{ pathPrefix }}/static/vendor/rickshaw/rickshaw.min.js"></script>
|
<script src="{{ pathPrefix }}/static/vendor/rickshaw/rickshaw.min.js?v={{ buildVersion }}"></script>
|
||||||
<script src="{{ pathPrefix }}/static/vendor/js/jquery.min.js"></script>
|
<script src="{{ pathPrefix }}/static/vendor/js/jquery.min.js?v={{ buildVersion }}"></script>
|
||||||
<script src="{{ pathPrefix }}/static/vendor/bootstrap-3.3.1/js/bootstrap.min.js"></script>
|
<script src="{{ pathPrefix }}/static/vendor/bootstrap-3.3.1/js/bootstrap.min.js?v={{ buildVersion }}"></script>
|
||||||
|
|
||||||
<script>var PATH_PREFIX = "{{ pathPrefix }}";</script>
|
<script>
|
||||||
<script src="{{ pathPrefix }}/static/js/prom_console.js"></script>
|
var PATH_PREFIX = "{{ pathPrefix }}";
|
||||||
|
var BUILD_VERSION = "{{ buildVersion }}";
|
||||||
|
</script>
|
||||||
|
<script src="{{ pathPrefix }}/static/js/prom_console.js?v={{ buildVersion }}"></script>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{/* Top of all pages. */}}
|
{{/* Top of all pages. */}}
|
||||||
|
|
|
@ -60,17 +60,17 @@ func init() {
|
||||||
prometheus.MustRegister(azureSDRefreshFailuresCount)
|
prometheus.MustRegister(azureSDRefreshFailuresCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AzureDiscovery periodically performs Azure-SD requests. It implements
|
// Discovery periodically performs Azure-SD requests. It implements
|
||||||
// the TargetProvider interface.
|
// the TargetProvider interface.
|
||||||
type AzureDiscovery struct {
|
type Discovery struct {
|
||||||
cfg *config.AzureSDConfig
|
cfg *config.AzureSDConfig
|
||||||
interval time.Duration
|
interval time.Duration
|
||||||
port int
|
port int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDiscovery returns a new AzureDiscovery which periodically refreshes its targets.
|
// NewDiscovery returns a new AzureDiscovery which periodically refreshes its targets.
|
||||||
func NewDiscovery(cfg *config.AzureSDConfig) *AzureDiscovery {
|
func NewDiscovery(cfg *config.AzureSDConfig) *Discovery {
|
||||||
return &AzureDiscovery{
|
return &Discovery{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
interval: time.Duration(cfg.RefreshInterval),
|
interval: time.Duration(cfg.RefreshInterval),
|
||||||
port: cfg.Port,
|
port: cfg.Port,
|
||||||
|
@ -78,8 +78,8 @@ func NewDiscovery(cfg *config.AzureSDConfig) *AzureDiscovery {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run implements the TargetProvider interface.
|
// Run implements the TargetProvider interface.
|
||||||
func (ad *AzureDiscovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
func (d *Discovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
||||||
ticker := time.NewTicker(ad.interval)
|
ticker := time.NewTicker(d.interval)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
@ -89,7 +89,7 @@ func (ad *AzureDiscovery) Run(ctx context.Context, ch chan<- []*config.TargetGro
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
tg, err := ad.refresh()
|
tg, err := d.refresh()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("unable to refresh during Azure discovery: %s", err)
|
log.Errorf("unable to refresh during Azure discovery: %s", err)
|
||||||
} else {
|
} else {
|
||||||
|
@ -156,7 +156,7 @@ func newAzureResourceFromID(id string) (azureResource, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ad *AzureDiscovery) refresh() (tg *config.TargetGroup, err error) {
|
func (d *Discovery) refresh() (tg *config.TargetGroup, err error) {
|
||||||
t0 := time.Now()
|
t0 := time.Now()
|
||||||
defer func() {
|
defer func() {
|
||||||
azureSDRefreshDuration.Observe(time.Since(t0).Seconds())
|
azureSDRefreshDuration.Observe(time.Since(t0).Seconds())
|
||||||
|
@ -165,7 +165,7 @@ func (ad *AzureDiscovery) refresh() (tg *config.TargetGroup, err error) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
tg = &config.TargetGroup{}
|
tg = &config.TargetGroup{}
|
||||||
client, err := createAzureClient(*ad.cfg)
|
client, err := createAzureClient(*d.cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return tg, fmt.Errorf("could not create Azure client: %s", err)
|
return tg, fmt.Errorf("could not create Azure client: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -246,7 +246,7 @@ func (ad *AzureDiscovery) refresh() (tg *config.TargetGroup, err error) {
|
||||||
for _, ip := range *networkInterface.Properties.IPConfigurations {
|
for _, ip := range *networkInterface.Properties.IPConfigurations {
|
||||||
if ip.Properties.PrivateIPAddress != nil {
|
if ip.Properties.PrivateIPAddress != nil {
|
||||||
labels[azureLabelMachinePrivateIP] = model.LabelValue(*ip.Properties.PrivateIPAddress)
|
labels[azureLabelMachinePrivateIP] = model.LabelValue(*ip.Properties.PrivateIPAddress)
|
||||||
address := net.JoinHostPort(*ip.Properties.PrivateIPAddress, fmt.Sprintf("%d", ad.port))
|
address := net.JoinHostPort(*ip.Properties.PrivateIPAddress, fmt.Sprintf("%d", d.port))
|
||||||
labels[model.AddressLabel] = model.LabelValue(address)
|
labels[model.AddressLabel] = model.LabelValue(address)
|
||||||
ch <- target{labelSet: labels, err: nil}
|
ch <- target{labelSet: labels, err: nil}
|
||||||
return
|
return
|
||||||
|
|
|
@ -117,12 +117,12 @@ func NewDiscovery(conf *config.ConsulSDConfig) (*Discovery, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// shouldWatch returns whether the service of the given name should be watched.
|
// shouldWatch returns whether the service of the given name should be watched.
|
||||||
func (cd *Discovery) shouldWatch(name string) bool {
|
func (d *Discovery) shouldWatch(name string) bool {
|
||||||
// If there's no fixed set of watched services, we watch everything.
|
// If there's no fixed set of watched services, we watch everything.
|
||||||
if len(cd.watchedServices) == 0 {
|
if len(d.watchedServices) == 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
for _, sn := range cd.watchedServices {
|
for _, sn := range d.watchedServices {
|
||||||
if sn == name {
|
if sn == name {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -131,13 +131,13 @@ func (cd *Discovery) shouldWatch(name string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run implements the TargetProvider interface.
|
// Run implements the TargetProvider interface.
|
||||||
func (cd *Discovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
func (d *Discovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
||||||
// Watched services and their cancelation functions.
|
// Watched services and their cancelation functions.
|
||||||
services := map[string]func(){}
|
services := map[string]func(){}
|
||||||
|
|
||||||
var lastIndex uint64
|
var lastIndex uint64
|
||||||
for {
|
for {
|
||||||
catalog := cd.client.Catalog()
|
catalog := d.client.Catalog()
|
||||||
t0 := time.Now()
|
t0 := time.Now()
|
||||||
srvs, meta, err := catalog.Services(&consul.QueryOptions{
|
srvs, meta, err := catalog.Services(&consul.QueryOptions{
|
||||||
WaitIndex: lastIndex,
|
WaitIndex: lastIndex,
|
||||||
|
@ -167,19 +167,19 @@ func (cd *Discovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
||||||
|
|
||||||
// If the datacenter was not set from clientConf, let's get it from the local Consul agent
|
// If the datacenter was not set from clientConf, let's get it from the local Consul agent
|
||||||
// (Consul default is to use local node's datacenter if one isn't given for a query).
|
// (Consul default is to use local node's datacenter if one isn't given for a query).
|
||||||
if cd.clientDatacenter == "" {
|
if d.clientDatacenter == "" {
|
||||||
info, err := cd.client.Agent().Self()
|
info, err := d.client.Agent().Self()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error retrieving datacenter name: %s", err)
|
log.Errorf("Error retrieving datacenter name: %s", err)
|
||||||
time.Sleep(retryInterval)
|
time.Sleep(retryInterval)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
cd.clientDatacenter = info["Config"]["Datacenter"].(string)
|
d.clientDatacenter = info["Config"]["Datacenter"].(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for new services.
|
// Check for new services.
|
||||||
for name := range srvs {
|
for name := range srvs {
|
||||||
if !cd.shouldWatch(name) {
|
if !d.shouldWatch(name) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if _, ok := services[name]; ok {
|
if _, ok := services[name]; ok {
|
||||||
|
@ -187,13 +187,13 @@ func (cd *Discovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
||||||
}
|
}
|
||||||
|
|
||||||
srv := &consulService{
|
srv := &consulService{
|
||||||
client: cd.client,
|
client: d.client,
|
||||||
name: name,
|
name: name,
|
||||||
labels: model.LabelSet{
|
labels: model.LabelSet{
|
||||||
serviceLabel: model.LabelValue(name),
|
serviceLabel: model.LabelValue(name),
|
||||||
datacenterLabel: model.LabelValue(cd.clientDatacenter),
|
datacenterLabel: model.LabelValue(d.clientDatacenter),
|
||||||
},
|
},
|
||||||
tagSeparator: cd.tagSeparator,
|
tagSeparator: d.tagSeparator,
|
||||||
}
|
}
|
||||||
|
|
||||||
wctx, cancel := context.WithCancel(ctx)
|
wctx, cancel := context.WithCancel(ctx)
|
||||||
|
|
48
discovery/consul/consul_test.go
Normal file
48
discovery/consul/consul_test.go
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
// Copyright 2015 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 consul
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/prometheus/prometheus/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConfiguredService(t *testing.T) {
|
||||||
|
conf := &config.ConsulSDConfig{
|
||||||
|
Services: []string{"configuredServiceName"}}
|
||||||
|
consulDiscovery, err := NewDiscovery(conf)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unexpected error when initialising discovery %v", err)
|
||||||
|
}
|
||||||
|
if !consulDiscovery.shouldWatch("configuredServiceName") {
|
||||||
|
t.Errorf("Expected service %s to be watched", "configuredServiceName")
|
||||||
|
}
|
||||||
|
if consulDiscovery.shouldWatch("nonConfiguredServiceName") {
|
||||||
|
t.Errorf("Expected service %s to not be watched", "nonConfiguredServiceName")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNonConfiguredService(t *testing.T) {
|
||||||
|
conf := &config.ConsulSDConfig{}
|
||||||
|
consulDiscovery, err := NewDiscovery(conf)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unexpected error when initialising discovery %v", err)
|
||||||
|
}
|
||||||
|
if !consulDiscovery.shouldWatch("nonConfiguredServiceName") {
|
||||||
|
t.Errorf("Expected service %s to be watched", "nonConfiguredServiceName")
|
||||||
|
}
|
||||||
|
}
|
|
@ -64,7 +64,6 @@ type Discovery struct {
|
||||||
names []string
|
names []string
|
||||||
|
|
||||||
interval time.Duration
|
interval time.Duration
|
||||||
m sync.RWMutex
|
|
||||||
port int
|
port int
|
||||||
qtype uint16
|
qtype uint16
|
||||||
}
|
}
|
||||||
|
@ -89,30 +88,30 @@ func NewDiscovery(conf *config.DNSSDConfig) *Discovery {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run implements the TargetProvider interface.
|
// Run implements the TargetProvider interface.
|
||||||
func (dd *Discovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
func (d *Discovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
||||||
ticker := time.NewTicker(dd.interval)
|
ticker := time.NewTicker(d.interval)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
// Get an initial set right away.
|
// Get an initial set right away.
|
||||||
dd.refreshAll(ctx, ch)
|
d.refreshAll(ctx, ch)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
dd.refreshAll(ctx, ch)
|
d.refreshAll(ctx, ch)
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dd *Discovery) refreshAll(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
func (d *Discovery) refreshAll(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
wg.Add(len(dd.names))
|
wg.Add(len(d.names))
|
||||||
for _, name := range dd.names {
|
for _, name := range d.names {
|
||||||
go func(n string) {
|
go func(n string) {
|
||||||
if err := dd.refresh(ctx, n, ch); err != nil {
|
if err := d.refresh(ctx, n, ch); err != nil {
|
||||||
log.Errorf("Error refreshing DNS targets: %s", err)
|
log.Errorf("Error refreshing DNS targets: %s", err)
|
||||||
}
|
}
|
||||||
wg.Done()
|
wg.Done()
|
||||||
|
@ -122,8 +121,8 @@ func (dd *Discovery) refreshAll(ctx context.Context, ch chan<- []*config.TargetG
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dd *Discovery) refresh(ctx context.Context, name string, ch chan<- []*config.TargetGroup) error {
|
func (d *Discovery) refresh(ctx context.Context, name string, ch chan<- []*config.TargetGroup) error {
|
||||||
response, err := lookupAll(name, dd.qtype)
|
response, err := lookupAll(name, d.qtype)
|
||||||
dnsSDLookupsCount.Inc()
|
dnsSDLookupsCount.Inc()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dnsSDLookupFailuresCount.Inc()
|
dnsSDLookupFailuresCount.Inc()
|
||||||
|
@ -144,9 +143,9 @@ func (dd *Discovery) refresh(ctx context.Context, name string, ch chan<- []*conf
|
||||||
|
|
||||||
target = hostPort(addr.Target, int(addr.Port))
|
target = hostPort(addr.Target, int(addr.Port))
|
||||||
case *dns.A:
|
case *dns.A:
|
||||||
target = hostPort(addr.A.String(), dd.port)
|
target = hostPort(addr.A.String(), d.port)
|
||||||
case *dns.AAAA:
|
case *dns.AAAA:
|
||||||
target = hostPort(addr.AAAA.String(), dd.port)
|
target = hostPort(addr.AAAA.String(), d.port)
|
||||||
default:
|
default:
|
||||||
log.Warnf("%q is not a valid SRV record", record)
|
log.Warnf("%q is not a valid SRV record", record)
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -65,9 +65,9 @@ func init() {
|
||||||
prometheus.MustRegister(ec2SDRefreshDuration)
|
prometheus.MustRegister(ec2SDRefreshDuration)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EC2Discovery periodically performs EC2-SD requests. It implements
|
// Discovery periodically performs EC2-SD requests. It implements
|
||||||
// the TargetProvider interface.
|
// the TargetProvider interface.
|
||||||
type EC2Discovery struct {
|
type Discovery struct {
|
||||||
aws *aws.Config
|
aws *aws.Config
|
||||||
interval time.Duration
|
interval time.Duration
|
||||||
profile string
|
profile string
|
||||||
|
@ -75,12 +75,12 @@ type EC2Discovery struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDiscovery returns a new EC2Discovery which periodically refreshes its targets.
|
// NewDiscovery returns a new EC2Discovery which periodically refreshes its targets.
|
||||||
func NewDiscovery(conf *config.EC2SDConfig) *EC2Discovery {
|
func NewDiscovery(conf *config.EC2SDConfig) *Discovery {
|
||||||
creds := credentials.NewStaticCredentials(conf.AccessKey, conf.SecretKey, "")
|
creds := credentials.NewStaticCredentials(conf.AccessKey, conf.SecretKey, "")
|
||||||
if conf.AccessKey == "" && conf.SecretKey == "" {
|
if conf.AccessKey == "" && conf.SecretKey == "" {
|
||||||
creds = nil
|
creds = nil
|
||||||
}
|
}
|
||||||
return &EC2Discovery{
|
return &Discovery{
|
||||||
aws: &aws.Config{
|
aws: &aws.Config{
|
||||||
Region: &conf.Region,
|
Region: &conf.Region,
|
||||||
Credentials: creds,
|
Credentials: creds,
|
||||||
|
@ -92,12 +92,12 @@ func NewDiscovery(conf *config.EC2SDConfig) *EC2Discovery {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run implements the TargetProvider interface.
|
// Run implements the TargetProvider interface.
|
||||||
func (ed *EC2Discovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
func (d *Discovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
||||||
ticker := time.NewTicker(ed.interval)
|
ticker := time.NewTicker(d.interval)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
// Get an initial set right away.
|
// Get an initial set right away.
|
||||||
tg, err := ed.refresh()
|
tg, err := d.refresh()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
} else {
|
} else {
|
||||||
|
@ -111,7 +111,7 @@ func (ed *EC2Discovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
tg, err := ed.refresh()
|
tg, err := d.refresh()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
continue
|
continue
|
||||||
|
@ -128,7 +128,7 @@ func (ed *EC2Discovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ed *EC2Discovery) refresh() (tg *config.TargetGroup, err error) {
|
func (d *Discovery) refresh() (tg *config.TargetGroup, err error) {
|
||||||
t0 := time.Now()
|
t0 := time.Now()
|
||||||
defer func() {
|
defer func() {
|
||||||
ec2SDRefreshDuration.Observe(time.Since(t0).Seconds())
|
ec2SDRefreshDuration.Observe(time.Since(t0).Seconds())
|
||||||
|
@ -138,8 +138,8 @@ func (ed *EC2Discovery) refresh() (tg *config.TargetGroup, err error) {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
sess, err := session.NewSessionWithOptions(session.Options{
|
sess, err := session.NewSessionWithOptions(session.Options{
|
||||||
Config: *ed.aws,
|
Config: *d.aws,
|
||||||
Profile: ed.profile,
|
Profile: d.profile,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not create aws session: %s", err)
|
return nil, fmt.Errorf("could not create aws session: %s", err)
|
||||||
|
@ -147,7 +147,7 @@ func (ed *EC2Discovery) refresh() (tg *config.TargetGroup, err error) {
|
||||||
|
|
||||||
ec2s := ec2.New(sess)
|
ec2s := ec2.New(sess)
|
||||||
tg = &config.TargetGroup{
|
tg = &config.TargetGroup{
|
||||||
Source: *ed.aws.Region,
|
Source: *d.aws.Region,
|
||||||
}
|
}
|
||||||
if err = ec2s.DescribeInstancesPages(nil, func(p *ec2.DescribeInstancesOutput, lastPage bool) bool {
|
if err = ec2s.DescribeInstancesPages(nil, func(p *ec2.DescribeInstancesOutput, lastPage bool) bool {
|
||||||
for _, r := range p.Reservations {
|
for _, r := range p.Reservations {
|
||||||
|
@ -159,7 +159,7 @@ func (ed *EC2Discovery) refresh() (tg *config.TargetGroup, err error) {
|
||||||
ec2LabelInstanceID: model.LabelValue(*inst.InstanceId),
|
ec2LabelInstanceID: model.LabelValue(*inst.InstanceId),
|
||||||
}
|
}
|
||||||
labels[ec2LabelPrivateIP] = model.LabelValue(*inst.PrivateIpAddress)
|
labels[ec2LabelPrivateIP] = model.LabelValue(*inst.PrivateIpAddress)
|
||||||
addr := net.JoinHostPort(*inst.PrivateIpAddress, fmt.Sprintf("%d", ed.port))
|
addr := net.JoinHostPort(*inst.PrivateIpAddress, fmt.Sprintf("%d", d.port))
|
||||||
labels[model.AddressLabel] = model.LabelValue(addr)
|
labels[model.AddressLabel] = model.LabelValue(addr)
|
||||||
|
|
||||||
if inst.PublicIpAddress != nil {
|
if inst.PublicIpAddress != nil {
|
||||||
|
|
|
@ -51,10 +51,10 @@ func init() {
|
||||||
prometheus.MustRegister(fileSDReadErrorsCount)
|
prometheus.MustRegister(fileSDReadErrorsCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileDiscovery provides service discovery functionality based
|
// Discovery provides service discovery functionality based
|
||||||
// on files that contain target groups in JSON or YAML format. Refreshing
|
// on files that contain target groups in JSON or YAML format. Refreshing
|
||||||
// happens using file watches and periodic refreshes.
|
// happens using file watches and periodic refreshes.
|
||||||
type FileDiscovery struct {
|
type Discovery struct {
|
||||||
paths []string
|
paths []string
|
||||||
watcher *fsnotify.Watcher
|
watcher *fsnotify.Watcher
|
||||||
interval time.Duration
|
interval time.Duration
|
||||||
|
@ -66,17 +66,17 @@ type FileDiscovery struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDiscovery returns a new file discovery for the given paths.
|
// NewDiscovery returns a new file discovery for the given paths.
|
||||||
func NewDiscovery(conf *config.FileSDConfig) *FileDiscovery {
|
func NewDiscovery(conf *config.FileSDConfig) *Discovery {
|
||||||
return &FileDiscovery{
|
return &Discovery{
|
||||||
paths: conf.Files,
|
paths: conf.Files,
|
||||||
interval: time.Duration(conf.RefreshInterval),
|
interval: time.Duration(conf.RefreshInterval),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// listFiles returns a list of all files that match the configured patterns.
|
// listFiles returns a list of all files that match the configured patterns.
|
||||||
func (fd *FileDiscovery) listFiles() []string {
|
func (d *Discovery) listFiles() []string {
|
||||||
var paths []string
|
var paths []string
|
||||||
for _, p := range fd.paths {
|
for _, p := range d.paths {
|
||||||
files, err := filepath.Glob(p)
|
files, err := filepath.Glob(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error expanding glob %q: %s", p, err)
|
log.Errorf("Error expanding glob %q: %s", p, err)
|
||||||
|
@ -89,36 +89,36 @@ func (fd *FileDiscovery) listFiles() []string {
|
||||||
|
|
||||||
// watchFiles sets watches on all full paths or directories that were configured for
|
// watchFiles sets watches on all full paths or directories that were configured for
|
||||||
// this file discovery.
|
// this file discovery.
|
||||||
func (fd *FileDiscovery) watchFiles() {
|
func (d *Discovery) watchFiles() {
|
||||||
if fd.watcher == nil {
|
if d.watcher == nil {
|
||||||
panic("no watcher configured")
|
panic("no watcher configured")
|
||||||
}
|
}
|
||||||
for _, p := range fd.paths {
|
for _, p := range d.paths {
|
||||||
if idx := strings.LastIndex(p, "/"); idx > -1 {
|
if idx := strings.LastIndex(p, "/"); idx > -1 {
|
||||||
p = p[:idx]
|
p = p[:idx]
|
||||||
} else {
|
} else {
|
||||||
p = "./"
|
p = "./"
|
||||||
}
|
}
|
||||||
if err := fd.watcher.Add(p); err != nil {
|
if err := d.watcher.Add(p); err != nil {
|
||||||
log.Errorf("Error adding file watch for %q: %s", p, err)
|
log.Errorf("Error adding file watch for %q: %s", p, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run implements the TargetProvider interface.
|
// Run implements the TargetProvider interface.
|
||||||
func (fd *FileDiscovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
func (d *Discovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
||||||
defer fd.stop()
|
defer d.stop()
|
||||||
|
|
||||||
watcher, err := fsnotify.NewWatcher()
|
watcher, err := fsnotify.NewWatcher()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error creating file watcher: %s", err)
|
log.Errorf("Error creating file watcher: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fd.watcher = watcher
|
d.watcher = watcher
|
||||||
|
|
||||||
fd.refresh(ctx, ch)
|
d.refresh(ctx, ch)
|
||||||
|
|
||||||
ticker := time.NewTicker(fd.interval)
|
ticker := time.NewTicker(d.interval)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
@ -126,7 +126,7 @@ func (fd *FileDiscovery) Run(ctx context.Context, ch chan<- []*config.TargetGrou
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
|
|
||||||
case event := <-fd.watcher.Events:
|
case event := <-d.watcher.Events:
|
||||||
// fsnotify sometimes sends a bunch of events without name or operation.
|
// fsnotify sometimes sends a bunch of events without name or operation.
|
||||||
// It's unclear what they are and why they are sent - filter them out.
|
// It's unclear what they are and why they are sent - filter them out.
|
||||||
if len(event.Name) == 0 {
|
if len(event.Name) == 0 {
|
||||||
|
@ -140,14 +140,14 @@ func (fd *FileDiscovery) Run(ctx context.Context, ch chan<- []*config.TargetGrou
|
||||||
// different combinations of operations. For all practical purposes
|
// different combinations of operations. For all practical purposes
|
||||||
// this is inaccurate.
|
// this is inaccurate.
|
||||||
// The most reliable solution is to reload everything if anything happens.
|
// The most reliable solution is to reload everything if anything happens.
|
||||||
fd.refresh(ctx, ch)
|
d.refresh(ctx, ch)
|
||||||
|
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
// Setting a new watch after an update might fail. Make sure we don't lose
|
// Setting a new watch after an update might fail. Make sure we don't lose
|
||||||
// those files forever.
|
// those files forever.
|
||||||
fd.refresh(ctx, ch)
|
d.refresh(ctx, ch)
|
||||||
|
|
||||||
case err := <-fd.watcher.Errors:
|
case err := <-d.watcher.Errors:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error on file watch: %s", err)
|
log.Errorf("Error on file watch: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -156,8 +156,8 @@ func (fd *FileDiscovery) Run(ctx context.Context, ch chan<- []*config.TargetGrou
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop shuts down the file watcher.
|
// stop shuts down the file watcher.
|
||||||
func (fd *FileDiscovery) stop() {
|
func (d *Discovery) stop() {
|
||||||
log.Debugf("Stopping file discovery for %s...", fd.paths)
|
log.Debugf("Stopping file discovery for %s...", d.paths)
|
||||||
|
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
defer close(done)
|
defer close(done)
|
||||||
|
@ -166,37 +166,37 @@ func (fd *FileDiscovery) stop() {
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-fd.watcher.Errors:
|
case <-d.watcher.Errors:
|
||||||
case <-fd.watcher.Events:
|
case <-d.watcher.Events:
|
||||||
// Drain all events and errors.
|
// Drain all events and errors.
|
||||||
case <-done:
|
case <-done:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
if err := fd.watcher.Close(); err != nil {
|
if err := d.watcher.Close(); err != nil {
|
||||||
log.Errorf("Error closing file watcher for %s: %s", fd.paths, err)
|
log.Errorf("Error closing file watcher for %s: %s", d.paths, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("File discovery for %s stopped.", fd.paths)
|
log.Debugf("File discovery for %s stopped.", d.paths)
|
||||||
}
|
}
|
||||||
|
|
||||||
// refresh reads all files matching the discovery's patterns and sends the respective
|
// refresh reads all files matching the discovery's patterns and sends the respective
|
||||||
// updated target groups through the channel.
|
// updated target groups through the channel.
|
||||||
func (fd *FileDiscovery) refresh(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
func (d *Discovery) refresh(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
||||||
t0 := time.Now()
|
t0 := time.Now()
|
||||||
defer func() {
|
defer func() {
|
||||||
fileSDScanDuration.Observe(time.Since(t0).Seconds())
|
fileSDScanDuration.Observe(time.Since(t0).Seconds())
|
||||||
}()
|
}()
|
||||||
|
|
||||||
ref := map[string]int{}
|
ref := map[string]int{}
|
||||||
for _, p := range fd.listFiles() {
|
for _, p := range d.listFiles() {
|
||||||
tgroups, err := readFile(p)
|
tgroups, err := readFile(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fileSDReadErrorsCount.Inc()
|
fileSDReadErrorsCount.Inc()
|
||||||
log.Errorf("Error reading file %q: %s", p, err)
|
log.Errorf("Error reading file %q: %s", p, err)
|
||||||
// Prevent deletion down below.
|
// Prevent deletion down below.
|
||||||
ref[p] = fd.lastRefresh[p]
|
ref[p] = d.lastRefresh[p]
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
|
@ -208,7 +208,7 @@ func (fd *FileDiscovery) refresh(ctx context.Context, ch chan<- []*config.Target
|
||||||
ref[p] = len(tgroups)
|
ref[p] = len(tgroups)
|
||||||
}
|
}
|
||||||
// Send empty updates for sources that disappeared.
|
// Send empty updates for sources that disappeared.
|
||||||
for f, n := range fd.lastRefresh {
|
for f, n := range d.lastRefresh {
|
||||||
m, ok := ref[f]
|
m, ok := ref[f]
|
||||||
if !ok || n > m {
|
if !ok || n > m {
|
||||||
for i := m; i < n; i++ {
|
for i := m; i < n; i++ {
|
||||||
|
@ -220,9 +220,9 @@ func (fd *FileDiscovery) refresh(ctx context.Context, ch chan<- []*config.Target
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fd.lastRefresh = ref
|
d.lastRefresh = ref
|
||||||
|
|
||||||
fd.watchFiles()
|
d.watchFiles()
|
||||||
}
|
}
|
||||||
|
|
||||||
// fileSource returns a source ID for the i-th target group in the file.
|
// fileSource returns a source ID for the i-th target group in the file.
|
||||||
|
|
|
@ -44,9 +44,6 @@ const (
|
||||||
gceLabelInstanceStatus = gceLabel + "instance_status"
|
gceLabelInstanceStatus = gceLabel + "instance_status"
|
||||||
gceLabelTags = gceLabel + "tags"
|
gceLabelTags = gceLabel + "tags"
|
||||||
gceLabelMetadata = gceLabel + "metadata_"
|
gceLabelMetadata = gceLabel + "metadata_"
|
||||||
|
|
||||||
// Constants for instrumentation.
|
|
||||||
namespace = "prometheus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -67,9 +64,9 @@ func init() {
|
||||||
prometheus.MustRegister(gceSDRefreshDuration)
|
prometheus.MustRegister(gceSDRefreshDuration)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GCEDiscovery periodically performs GCE-SD requests. It implements
|
// Discovery periodically performs GCE-SD requests. It implements
|
||||||
// the TargetProvider interface.
|
// the TargetProvider interface.
|
||||||
type GCEDiscovery struct {
|
type Discovery struct {
|
||||||
project string
|
project string
|
||||||
zone string
|
zone string
|
||||||
filter string
|
filter string
|
||||||
|
@ -81,9 +78,9 @@ type GCEDiscovery struct {
|
||||||
tagSeparator string
|
tagSeparator string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewGCEDiscovery returns a new GCEDiscovery which periodically refreshes its targets.
|
// NewDiscovery returns a new Discovery which periodically refreshes its targets.
|
||||||
func NewDiscovery(conf *config.GCESDConfig) (*GCEDiscovery, error) {
|
func NewDiscovery(conf *config.GCESDConfig) (*Discovery, error) {
|
||||||
gd := &GCEDiscovery{
|
gd := &Discovery{
|
||||||
project: conf.Project,
|
project: conf.Project,
|
||||||
zone: conf.Zone,
|
zone: conf.Zone,
|
||||||
filter: conf.Filter,
|
filter: conf.Filter,
|
||||||
|
@ -105,9 +102,9 @@ func NewDiscovery(conf *config.GCESDConfig) (*GCEDiscovery, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run implements the TargetProvider interface.
|
// Run implements the TargetProvider interface.
|
||||||
func (gd *GCEDiscovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
func (d *Discovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
||||||
// Get an initial set right away.
|
// Get an initial set right away.
|
||||||
tg, err := gd.refresh()
|
tg, err := d.refresh()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
} else {
|
} else {
|
||||||
|
@ -117,13 +114,13 @@ func (gd *GCEDiscovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ticker := time.NewTicker(gd.interval)
|
ticker := time.NewTicker(d.interval)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
tg, err := gd.refresh()
|
tg, err := d.refresh()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
continue
|
continue
|
||||||
|
@ -138,7 +135,7 @@ func (gd *GCEDiscovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gd *GCEDiscovery) refresh() (tg *config.TargetGroup, err error) {
|
func (d *Discovery) refresh() (tg *config.TargetGroup, err error) {
|
||||||
t0 := time.Now()
|
t0 := time.Now()
|
||||||
defer func() {
|
defer func() {
|
||||||
gceSDRefreshDuration.Observe(time.Since(t0).Seconds())
|
gceSDRefreshDuration.Observe(time.Since(t0).Seconds())
|
||||||
|
@ -148,12 +145,12 @@ func (gd *GCEDiscovery) refresh() (tg *config.TargetGroup, err error) {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
tg = &config.TargetGroup{
|
tg = &config.TargetGroup{
|
||||||
Source: fmt.Sprintf("GCE_%s_%s", gd.project, gd.zone),
|
Source: fmt.Sprintf("GCE_%s_%s", d.project, d.zone),
|
||||||
}
|
}
|
||||||
|
|
||||||
ilc := gd.isvc.List(gd.project, gd.zone)
|
ilc := d.isvc.List(d.project, d.zone)
|
||||||
if len(gd.filter) > 0 {
|
if len(d.filter) > 0 {
|
||||||
ilc = ilc.Filter(gd.filter)
|
ilc = ilc.Filter(d.filter)
|
||||||
}
|
}
|
||||||
err = ilc.Pages(nil, func(l *compute.InstanceList) error {
|
err = ilc.Pages(nil, func(l *compute.InstanceList) error {
|
||||||
for _, inst := range l.Items {
|
for _, inst := range l.Items {
|
||||||
|
@ -161,7 +158,7 @@ func (gd *GCEDiscovery) refresh() (tg *config.TargetGroup, err error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
labels := model.LabelSet{
|
labels := model.LabelSet{
|
||||||
gceLabelProject: model.LabelValue(gd.project),
|
gceLabelProject: model.LabelValue(d.project),
|
||||||
gceLabelZone: model.LabelValue(inst.Zone),
|
gceLabelZone: model.LabelValue(inst.Zone),
|
||||||
gceLabelInstanceName: model.LabelValue(inst.Name),
|
gceLabelInstanceName: model.LabelValue(inst.Name),
|
||||||
gceLabelInstanceStatus: model.LabelValue(inst.Status),
|
gceLabelInstanceStatus: model.LabelValue(inst.Status),
|
||||||
|
@ -170,14 +167,14 @@ func (gd *GCEDiscovery) refresh() (tg *config.TargetGroup, err error) {
|
||||||
labels[gceLabelNetwork] = model.LabelValue(priIface.Network)
|
labels[gceLabelNetwork] = model.LabelValue(priIface.Network)
|
||||||
labels[gceLabelSubnetwork] = model.LabelValue(priIface.Subnetwork)
|
labels[gceLabelSubnetwork] = model.LabelValue(priIface.Subnetwork)
|
||||||
labels[gceLabelPrivateIP] = model.LabelValue(priIface.NetworkIP)
|
labels[gceLabelPrivateIP] = model.LabelValue(priIface.NetworkIP)
|
||||||
addr := fmt.Sprintf("%s:%d", priIface.NetworkIP, gd.port)
|
addr := fmt.Sprintf("%s:%d", priIface.NetworkIP, d.port)
|
||||||
labels[model.AddressLabel] = model.LabelValue(addr)
|
labels[model.AddressLabel] = model.LabelValue(addr)
|
||||||
|
|
||||||
// Tags in GCE are usually only used for networking rules.
|
// Tags in GCE are usually only used for networking rules.
|
||||||
if inst.Tags != nil && len(inst.Tags.Items) > 0 {
|
if inst.Tags != nil && len(inst.Tags.Items) > 0 {
|
||||||
// We surround the separated list with the separator as well. This way regular expressions
|
// We surround the separated list with the separator as well. This way regular expressions
|
||||||
// in relabeling rules don't have to consider tag positions.
|
// in relabeling rules don't have to consider tag positions.
|
||||||
tags := gd.tagSeparator + strings.Join(inst.Tags.Items, gd.tagSeparator) + gd.tagSeparator
|
tags := d.tagSeparator + strings.Join(inst.Tags.Items, d.tagSeparator) + d.tagSeparator
|
||||||
labels[gceLabelTags] = model.LabelValue(tags)
|
labels[gceLabelTags] = model.LabelValue(tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,33 +45,33 @@ func makeEndpoints() *v1.Endpoints {
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
},
|
},
|
||||||
Subsets: []v1.EndpointSubset{
|
Subsets: []v1.EndpointSubset{
|
||||||
v1.EndpointSubset{
|
{
|
||||||
Addresses: []v1.EndpointAddress{
|
Addresses: []v1.EndpointAddress{
|
||||||
v1.EndpointAddress{
|
{
|
||||||
IP: "1.2.3.4",
|
IP: "1.2.3.4",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Ports: []v1.EndpointPort{
|
Ports: []v1.EndpointPort{
|
||||||
v1.EndpointPort{
|
{
|
||||||
Name: "testport",
|
Name: "testport",
|
||||||
Port: 9000,
|
Port: 9000,
|
||||||
Protocol: v1.ProtocolTCP,
|
Protocol: v1.ProtocolTCP,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
v1.EndpointSubset{
|
{
|
||||||
Addresses: []v1.EndpointAddress{
|
Addresses: []v1.EndpointAddress{
|
||||||
v1.EndpointAddress{
|
{
|
||||||
IP: "2.3.4.5",
|
IP: "2.3.4.5",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
NotReadyAddresses: []v1.EndpointAddress{
|
NotReadyAddresses: []v1.EndpointAddress{
|
||||||
v1.EndpointAddress{
|
{
|
||||||
IP: "2.3.4.5",
|
IP: "2.3.4.5",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Ports: []v1.EndpointPort{
|
Ports: []v1.EndpointPort{
|
||||||
v1.EndpointPort{
|
{
|
||||||
Name: "testport",
|
Name: "testport",
|
||||||
Port: 9001,
|
Port: 9001,
|
||||||
Protocol: v1.ProtocolTCP,
|
Protocol: v1.ProtocolTCP,
|
||||||
|
@ -89,21 +89,21 @@ func TestEndpointsDiscoveryInitial(t *testing.T) {
|
||||||
k8sDiscoveryTest{
|
k8sDiscoveryTest{
|
||||||
discovery: n,
|
discovery: n,
|
||||||
expectedInitial: []*config.TargetGroup{
|
expectedInitial: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Targets: []model.LabelSet{
|
Targets: []model.LabelSet{
|
||||||
model.LabelSet{
|
{
|
||||||
"__address__": "1.2.3.4:9000",
|
"__address__": "1.2.3.4:9000",
|
||||||
"__meta_kubernetes_endpoint_port_name": "testport",
|
"__meta_kubernetes_endpoint_port_name": "testport",
|
||||||
"__meta_kubernetes_endpoint_port_protocol": "TCP",
|
"__meta_kubernetes_endpoint_port_protocol": "TCP",
|
||||||
"__meta_kubernetes_endpoint_ready": "true",
|
"__meta_kubernetes_endpoint_ready": "true",
|
||||||
},
|
},
|
||||||
model.LabelSet{
|
{
|
||||||
"__address__": "2.3.4.5:9001",
|
"__address__": "2.3.4.5:9001",
|
||||||
"__meta_kubernetes_endpoint_port_name": "testport",
|
"__meta_kubernetes_endpoint_port_name": "testport",
|
||||||
"__meta_kubernetes_endpoint_port_protocol": "TCP",
|
"__meta_kubernetes_endpoint_port_protocol": "TCP",
|
||||||
"__meta_kubernetes_endpoint_ready": "true",
|
"__meta_kubernetes_endpoint_ready": "true",
|
||||||
},
|
},
|
||||||
model.LabelSet{
|
{
|
||||||
"__address__": "2.3.4.5:9001",
|
"__address__": "2.3.4.5:9001",
|
||||||
"__meta_kubernetes_endpoint_port_name": "testport",
|
"__meta_kubernetes_endpoint_port_name": "testport",
|
||||||
"__meta_kubernetes_endpoint_port_protocol": "TCP",
|
"__meta_kubernetes_endpoint_port_protocol": "TCP",
|
||||||
|
@ -130,20 +130,20 @@ func TestEndpointsDiscoveryAdd(t *testing.T) {
|
||||||
Spec: v1.PodSpec{
|
Spec: v1.PodSpec{
|
||||||
NodeName: "testnode",
|
NodeName: "testnode",
|
||||||
Containers: []v1.Container{
|
Containers: []v1.Container{
|
||||||
v1.Container{
|
{
|
||||||
Name: "c1",
|
Name: "c1",
|
||||||
Ports: []v1.ContainerPort{
|
Ports: []v1.ContainerPort{
|
||||||
v1.ContainerPort{
|
{
|
||||||
Name: "mainport",
|
Name: "mainport",
|
||||||
ContainerPort: 9000,
|
ContainerPort: 9000,
|
||||||
Protocol: v1.ProtocolTCP,
|
Protocol: v1.ProtocolTCP,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
v1.Container{
|
{
|
||||||
Name: "c2",
|
Name: "c2",
|
||||||
Ports: []v1.ContainerPort{
|
Ports: []v1.ContainerPort{
|
||||||
v1.ContainerPort{
|
{
|
||||||
Name: "sideport",
|
Name: "sideport",
|
||||||
ContainerPort: 9001,
|
ContainerPort: 9001,
|
||||||
Protocol: v1.ProtocolTCP,
|
Protocol: v1.ProtocolTCP,
|
||||||
|
@ -169,9 +169,9 @@ func TestEndpointsDiscoveryAdd(t *testing.T) {
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
},
|
},
|
||||||
Subsets: []v1.EndpointSubset{
|
Subsets: []v1.EndpointSubset{
|
||||||
v1.EndpointSubset{
|
{
|
||||||
Addresses: []v1.EndpointAddress{
|
Addresses: []v1.EndpointAddress{
|
||||||
v1.EndpointAddress{
|
{
|
||||||
IP: "4.3.2.1",
|
IP: "4.3.2.1",
|
||||||
TargetRef: &v1.ObjectReference{
|
TargetRef: &v1.ObjectReference{
|
||||||
Kind: "Pod",
|
Kind: "Pod",
|
||||||
|
@ -181,7 +181,7 @@ func TestEndpointsDiscoveryAdd(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Ports: []v1.EndpointPort{
|
Ports: []v1.EndpointPort{
|
||||||
v1.EndpointPort{
|
{
|
||||||
Name: "testport",
|
Name: "testport",
|
||||||
Port: 9000,
|
Port: 9000,
|
||||||
Protocol: v1.ProtocolTCP,
|
Protocol: v1.ProtocolTCP,
|
||||||
|
@ -194,9 +194,9 @@ func TestEndpointsDiscoveryAdd(t *testing.T) {
|
||||||
}()
|
}()
|
||||||
},
|
},
|
||||||
expectedRes: []*config.TargetGroup{
|
expectedRes: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Targets: []model.LabelSet{
|
Targets: []model.LabelSet{
|
||||||
model.LabelSet{
|
{
|
||||||
"__address__": "4.3.2.1:9000",
|
"__address__": "4.3.2.1:9000",
|
||||||
"__meta_kubernetes_endpoint_port_name": "testport",
|
"__meta_kubernetes_endpoint_port_name": "testport",
|
||||||
"__meta_kubernetes_endpoint_port_protocol": "TCP",
|
"__meta_kubernetes_endpoint_port_protocol": "TCP",
|
||||||
|
@ -211,7 +211,7 @@ func TestEndpointsDiscoveryAdd(t *testing.T) {
|
||||||
"__meta_kubernetes_pod_container_port_number": "9000",
|
"__meta_kubernetes_pod_container_port_number": "9000",
|
||||||
"__meta_kubernetes_pod_container_port_protocol": "TCP",
|
"__meta_kubernetes_pod_container_port_protocol": "TCP",
|
||||||
},
|
},
|
||||||
model.LabelSet{
|
{
|
||||||
"__address__": "1.2.3.4:9001",
|
"__address__": "1.2.3.4:9001",
|
||||||
"__meta_kubernetes_pod_name": "testpod",
|
"__meta_kubernetes_pod_name": "testpod",
|
||||||
"__meta_kubernetes_pod_ip": "1.2.3.4",
|
"__meta_kubernetes_pod_ip": "1.2.3.4",
|
||||||
|
@ -242,7 +242,7 @@ func TestEndpointsDiscoveryDelete(t *testing.T) {
|
||||||
discovery: n,
|
discovery: n,
|
||||||
afterStart: func() { go func() { eps.Delete(makeEndpoints()) }() },
|
afterStart: func() { go func() { eps.Delete(makeEndpoints()) }() },
|
||||||
expectedRes: []*config.TargetGroup{
|
expectedRes: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Source: "endpoints/default/testendpoints",
|
Source: "endpoints/default/testendpoints",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -257,7 +257,7 @@ func TestEndpointsDiscoveryDeleteUnknownCacheState(t *testing.T) {
|
||||||
discovery: n,
|
discovery: n,
|
||||||
afterStart: func() { go func() { eps.Delete(cache.DeletedFinalStateUnknown{Obj: makeEndpoints()}) }() },
|
afterStart: func() { go func() { eps.Delete(cache.DeletedFinalStateUnknown{Obj: makeEndpoints()}) }() },
|
||||||
expectedRes: []*config.TargetGroup{
|
expectedRes: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Source: "endpoints/default/testendpoints",
|
Source: "endpoints/default/testendpoints",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -278,28 +278,28 @@ func TestEndpointsDiscoveryUpdate(t *testing.T) {
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
},
|
},
|
||||||
Subsets: []v1.EndpointSubset{
|
Subsets: []v1.EndpointSubset{
|
||||||
v1.EndpointSubset{
|
{
|
||||||
Addresses: []v1.EndpointAddress{
|
Addresses: []v1.EndpointAddress{
|
||||||
v1.EndpointAddress{
|
{
|
||||||
IP: "1.2.3.4",
|
IP: "1.2.3.4",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Ports: []v1.EndpointPort{
|
Ports: []v1.EndpointPort{
|
||||||
v1.EndpointPort{
|
{
|
||||||
Name: "testport",
|
Name: "testport",
|
||||||
Port: 9000,
|
Port: 9000,
|
||||||
Protocol: v1.ProtocolTCP,
|
Protocol: v1.ProtocolTCP,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
v1.EndpointSubset{
|
{
|
||||||
Addresses: []v1.EndpointAddress{
|
Addresses: []v1.EndpointAddress{
|
||||||
v1.EndpointAddress{
|
{
|
||||||
IP: "2.3.4.5",
|
IP: "2.3.4.5",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Ports: []v1.EndpointPort{
|
Ports: []v1.EndpointPort{
|
||||||
v1.EndpointPort{
|
{
|
||||||
Name: "testport",
|
Name: "testport",
|
||||||
Port: 9001,
|
Port: 9001,
|
||||||
Protocol: v1.ProtocolTCP,
|
Protocol: v1.ProtocolTCP,
|
||||||
|
@ -311,15 +311,15 @@ func TestEndpointsDiscoveryUpdate(t *testing.T) {
|
||||||
}()
|
}()
|
||||||
},
|
},
|
||||||
expectedRes: []*config.TargetGroup{
|
expectedRes: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Targets: []model.LabelSet{
|
Targets: []model.LabelSet{
|
||||||
model.LabelSet{
|
{
|
||||||
"__address__": "1.2.3.4:9000",
|
"__address__": "1.2.3.4:9000",
|
||||||
"__meta_kubernetes_endpoint_port_name": "testport",
|
"__meta_kubernetes_endpoint_port_name": "testport",
|
||||||
"__meta_kubernetes_endpoint_port_protocol": "TCP",
|
"__meta_kubernetes_endpoint_port_protocol": "TCP",
|
||||||
"__meta_kubernetes_endpoint_ready": "true",
|
"__meta_kubernetes_endpoint_ready": "true",
|
||||||
},
|
},
|
||||||
model.LabelSet{
|
{
|
||||||
"__address__": "2.3.4.5:9001",
|
"__address__": "2.3.4.5:9001",
|
||||||
"__meta_kubernetes_endpoint_port_name": "testport",
|
"__meta_kubernetes_endpoint_port_name": "testport",
|
||||||
"__meta_kubernetes_endpoint_port_protocol": "TCP",
|
"__meta_kubernetes_endpoint_port_protocol": "TCP",
|
||||||
|
|
|
@ -59,9 +59,9 @@ func init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kubernetes implements the TargetProvider interface for discovering
|
// Discovery implements the TargetProvider interface for discovering
|
||||||
// targets from Kubernetes.
|
// targets from Kubernetes.
|
||||||
type Kubernetes struct {
|
type Discovery struct {
|
||||||
client kubernetes.Interface
|
client kubernetes.Interface
|
||||||
role config.KubernetesRole
|
role config.KubernetesRole
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
|
@ -76,7 +76,7 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new Kubernetes discovery for the given role.
|
// New creates a new Kubernetes discovery for the given role.
|
||||||
func New(l log.Logger, conf *config.KubernetesSDConfig) (*Kubernetes, error) {
|
func New(l log.Logger, conf *config.KubernetesSDConfig) (*Discovery, error) {
|
||||||
var (
|
var (
|
||||||
kcfg *rest.Config
|
kcfg *rest.Config
|
||||||
err error
|
err error
|
||||||
|
@ -136,7 +136,7 @@ func New(l log.Logger, conf *config.KubernetesSDConfig) (*Kubernetes, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &Kubernetes{
|
return &Discovery{
|
||||||
client: c,
|
client: c,
|
||||||
logger: l,
|
logger: l,
|
||||||
role: conf.Role,
|
role: conf.Role,
|
||||||
|
@ -146,16 +146,16 @@ func New(l log.Logger, conf *config.KubernetesSDConfig) (*Kubernetes, error) {
|
||||||
const resyncPeriod = 10 * time.Minute
|
const resyncPeriod = 10 * time.Minute
|
||||||
|
|
||||||
// Run implements the TargetProvider interface.
|
// Run implements the TargetProvider interface.
|
||||||
func (k *Kubernetes) Run(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
func (d *Discovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
||||||
rclient := k.client.Core().GetRESTClient()
|
rclient := d.client.Core().GetRESTClient()
|
||||||
|
|
||||||
switch k.role {
|
switch d.role {
|
||||||
case "endpoints":
|
case "endpoints":
|
||||||
elw := cache.NewListWatchFromClient(rclient, "endpoints", api.NamespaceAll, nil)
|
elw := cache.NewListWatchFromClient(rclient, "endpoints", api.NamespaceAll, nil)
|
||||||
slw := cache.NewListWatchFromClient(rclient, "services", api.NamespaceAll, nil)
|
slw := cache.NewListWatchFromClient(rclient, "services", api.NamespaceAll, nil)
|
||||||
plw := cache.NewListWatchFromClient(rclient, "pods", api.NamespaceAll, nil)
|
plw := cache.NewListWatchFromClient(rclient, "pods", api.NamespaceAll, nil)
|
||||||
eps := NewEndpoints(
|
eps := NewEndpoints(
|
||||||
k.logger.With("kubernetes_sd", "endpoint"),
|
d.logger.With("kubernetes_sd", "endpoint"),
|
||||||
cache.NewSharedInformer(slw, &apiv1.Service{}, resyncPeriod),
|
cache.NewSharedInformer(slw, &apiv1.Service{}, resyncPeriod),
|
||||||
cache.NewSharedInformer(elw, &apiv1.Endpoints{}, resyncPeriod),
|
cache.NewSharedInformer(elw, &apiv1.Endpoints{}, resyncPeriod),
|
||||||
cache.NewSharedInformer(plw, &apiv1.Pod{}, resyncPeriod),
|
cache.NewSharedInformer(plw, &apiv1.Pod{}, resyncPeriod),
|
||||||
|
@ -178,7 +178,7 @@ func (k *Kubernetes) Run(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
||||||
case "pod":
|
case "pod":
|
||||||
plw := cache.NewListWatchFromClient(rclient, "pods", api.NamespaceAll, nil)
|
plw := cache.NewListWatchFromClient(rclient, "pods", api.NamespaceAll, nil)
|
||||||
pod := NewPod(
|
pod := NewPod(
|
||||||
k.logger.With("kubernetes_sd", "pod"),
|
d.logger.With("kubernetes_sd", "pod"),
|
||||||
cache.NewSharedInformer(plw, &apiv1.Pod{}, resyncPeriod),
|
cache.NewSharedInformer(plw, &apiv1.Pod{}, resyncPeriod),
|
||||||
)
|
)
|
||||||
go pod.informer.Run(ctx.Done())
|
go pod.informer.Run(ctx.Done())
|
||||||
|
@ -191,7 +191,7 @@ func (k *Kubernetes) Run(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
||||||
case "service":
|
case "service":
|
||||||
slw := cache.NewListWatchFromClient(rclient, "services", api.NamespaceAll, nil)
|
slw := cache.NewListWatchFromClient(rclient, "services", api.NamespaceAll, nil)
|
||||||
svc := NewService(
|
svc := NewService(
|
||||||
k.logger.With("kubernetes_sd", "service"),
|
d.logger.With("kubernetes_sd", "service"),
|
||||||
cache.NewSharedInformer(slw, &apiv1.Service{}, resyncPeriod),
|
cache.NewSharedInformer(slw, &apiv1.Service{}, resyncPeriod),
|
||||||
)
|
)
|
||||||
go svc.informer.Run(ctx.Done())
|
go svc.informer.Run(ctx.Done())
|
||||||
|
@ -204,7 +204,7 @@ func (k *Kubernetes) Run(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
||||||
case "node":
|
case "node":
|
||||||
nlw := cache.NewListWatchFromClient(rclient, "nodes", api.NamespaceAll, nil)
|
nlw := cache.NewListWatchFromClient(rclient, "nodes", api.NamespaceAll, nil)
|
||||||
node := NewNode(
|
node := NewNode(
|
||||||
k.logger.With("kubernetes_sd", "node"),
|
d.logger.With("kubernetes_sd", "node"),
|
||||||
cache.NewSharedInformer(nlw, &apiv1.Node{}, resyncPeriod),
|
cache.NewSharedInformer(nlw, &apiv1.Node{}, resyncPeriod),
|
||||||
)
|
)
|
||||||
go node.informer.Run(ctx.Done())
|
go node.informer.Run(ctx.Done())
|
||||||
|
@ -215,7 +215,7 @@ func (k *Kubernetes) Run(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
||||||
node.Run(ctx, ch)
|
node.Run(ctx, ch)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
k.logger.Errorf("unknown Kubernetes discovery kind %q", k.role)
|
d.logger.Errorf("unknown Kubernetes discovery kind %q", d.role)
|
||||||
}
|
}
|
||||||
|
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
|
|
|
@ -167,7 +167,7 @@ func makeNode(name, address string, labels map[string]string, annotations map[st
|
||||||
},
|
},
|
||||||
Status: v1.NodeStatus{
|
Status: v1.NodeStatus{
|
||||||
Addresses: []v1.NodeAddress{
|
Addresses: []v1.NodeAddress{
|
||||||
v1.NodeAddress{
|
{
|
||||||
Type: v1.NodeInternalIP,
|
Type: v1.NodeInternalIP,
|
||||||
Address: address,
|
Address: address,
|
||||||
},
|
},
|
||||||
|
@ -197,9 +197,9 @@ func TestNodeDiscoveryInitial(t *testing.T) {
|
||||||
k8sDiscoveryTest{
|
k8sDiscoveryTest{
|
||||||
discovery: n,
|
discovery: n,
|
||||||
expectedInitial: []*config.TargetGroup{
|
expectedInitial: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Targets: []model.LabelSet{
|
Targets: []model.LabelSet{
|
||||||
model.LabelSet{
|
{
|
||||||
"__address__": "1.2.3.4:10250",
|
"__address__": "1.2.3.4:10250",
|
||||||
"instance": "test",
|
"instance": "test",
|
||||||
"__meta_kubernetes_node_address_InternalIP": "1.2.3.4",
|
"__meta_kubernetes_node_address_InternalIP": "1.2.3.4",
|
||||||
|
@ -223,9 +223,9 @@ func TestNodeDiscoveryAdd(t *testing.T) {
|
||||||
discovery: n,
|
discovery: n,
|
||||||
afterStart: func() { go func() { i.Add(makeEnumeratedNode(1)) }() },
|
afterStart: func() { go func() { i.Add(makeEnumeratedNode(1)) }() },
|
||||||
expectedRes: []*config.TargetGroup{
|
expectedRes: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Targets: []model.LabelSet{
|
Targets: []model.LabelSet{
|
||||||
model.LabelSet{
|
{
|
||||||
"__address__": "1.2.3.4:10250",
|
"__address__": "1.2.3.4:10250",
|
||||||
"instance": "test1",
|
"instance": "test1",
|
||||||
"__meta_kubernetes_node_address_InternalIP": "1.2.3.4",
|
"__meta_kubernetes_node_address_InternalIP": "1.2.3.4",
|
||||||
|
@ -248,9 +248,9 @@ func TestNodeDiscoveryDelete(t *testing.T) {
|
||||||
discovery: n,
|
discovery: n,
|
||||||
afterStart: func() { go func() { i.Delete(makeEnumeratedNode(0)) }() },
|
afterStart: func() { go func() { i.Delete(makeEnumeratedNode(0)) }() },
|
||||||
expectedInitial: []*config.TargetGroup{
|
expectedInitial: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Targets: []model.LabelSet{
|
Targets: []model.LabelSet{
|
||||||
model.LabelSet{
|
{
|
||||||
"__address__": "1.2.3.4:10250",
|
"__address__": "1.2.3.4:10250",
|
||||||
"instance": "test0",
|
"instance": "test0",
|
||||||
"__meta_kubernetes_node_address_InternalIP": "1.2.3.4",
|
"__meta_kubernetes_node_address_InternalIP": "1.2.3.4",
|
||||||
|
@ -263,7 +263,7 @@ func TestNodeDiscoveryDelete(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedRes: []*config.TargetGroup{
|
expectedRes: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Source: "node/test0",
|
Source: "node/test0",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -278,9 +278,9 @@ func TestNodeDiscoveryDeleteUnknownCacheState(t *testing.T) {
|
||||||
discovery: n,
|
discovery: n,
|
||||||
afterStart: func() { go func() { i.Delete(cache.DeletedFinalStateUnknown{Obj: makeEnumeratedNode(0)}) }() },
|
afterStart: func() { go func() { i.Delete(cache.DeletedFinalStateUnknown{Obj: makeEnumeratedNode(0)}) }() },
|
||||||
expectedInitial: []*config.TargetGroup{
|
expectedInitial: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Targets: []model.LabelSet{
|
Targets: []model.LabelSet{
|
||||||
model.LabelSet{
|
{
|
||||||
"__address__": "1.2.3.4:10250",
|
"__address__": "1.2.3.4:10250",
|
||||||
"instance": "test0",
|
"instance": "test0",
|
||||||
"__meta_kubernetes_node_address_InternalIP": "1.2.3.4",
|
"__meta_kubernetes_node_address_InternalIP": "1.2.3.4",
|
||||||
|
@ -293,7 +293,7 @@ func TestNodeDiscoveryDeleteUnknownCacheState(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedRes: []*config.TargetGroup{
|
expectedRes: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Source: "node/test0",
|
Source: "node/test0",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -319,9 +319,9 @@ func TestNodeDiscoveryUpdate(t *testing.T) {
|
||||||
}()
|
}()
|
||||||
},
|
},
|
||||||
expectedInitial: []*config.TargetGroup{
|
expectedInitial: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Targets: []model.LabelSet{
|
Targets: []model.LabelSet{
|
||||||
model.LabelSet{
|
{
|
||||||
"__address__": "1.2.3.4:10250",
|
"__address__": "1.2.3.4:10250",
|
||||||
"instance": "test0",
|
"instance": "test0",
|
||||||
"__meta_kubernetes_node_address_InternalIP": "1.2.3.4",
|
"__meta_kubernetes_node_address_InternalIP": "1.2.3.4",
|
||||||
|
@ -334,9 +334,9 @@ func TestNodeDiscoveryUpdate(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedRes: []*config.TargetGroup{
|
expectedRes: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Targets: []model.LabelSet{
|
Targets: []model.LabelSet{
|
||||||
model.LabelSet{
|
{
|
||||||
"__address__": "1.2.3.4:10250",
|
"__address__": "1.2.3.4:10250",
|
||||||
"instance": "test0",
|
"instance": "test0",
|
||||||
"__meta_kubernetes_node_address_InternalIP": "1.2.3.4",
|
"__meta_kubernetes_node_address_InternalIP": "1.2.3.4",
|
||||||
|
|
|
@ -47,22 +47,22 @@ func makeMultiPortPod() *v1.Pod {
|
||||||
Spec: v1.PodSpec{
|
Spec: v1.PodSpec{
|
||||||
NodeName: "testnode",
|
NodeName: "testnode",
|
||||||
Containers: []v1.Container{
|
Containers: []v1.Container{
|
||||||
v1.Container{
|
{
|
||||||
Name: "testcontainer0",
|
Name: "testcontainer0",
|
||||||
Ports: []v1.ContainerPort{
|
Ports: []v1.ContainerPort{
|
||||||
v1.ContainerPort{
|
{
|
||||||
Name: "testport0",
|
Name: "testport0",
|
||||||
Protocol: v1.ProtocolTCP,
|
Protocol: v1.ProtocolTCP,
|
||||||
ContainerPort: int32(9000),
|
ContainerPort: int32(9000),
|
||||||
},
|
},
|
||||||
v1.ContainerPort{
|
{
|
||||||
Name: "testport1",
|
Name: "testport1",
|
||||||
Protocol: v1.ProtocolUDP,
|
Protocol: v1.ProtocolUDP,
|
||||||
ContainerPort: int32(9001),
|
ContainerPort: int32(9001),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
v1.Container{
|
{
|
||||||
Name: "testcontainer1",
|
Name: "testcontainer1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -71,7 +71,7 @@ func makeMultiPortPod() *v1.Pod {
|
||||||
PodIP: "1.2.3.4",
|
PodIP: "1.2.3.4",
|
||||||
HostIP: "2.3.4.5",
|
HostIP: "2.3.4.5",
|
||||||
Conditions: []v1.PodCondition{
|
Conditions: []v1.PodCondition{
|
||||||
v1.PodCondition{
|
{
|
||||||
Type: v1.PodReady,
|
Type: v1.PodReady,
|
||||||
Status: v1.ConditionTrue,
|
Status: v1.ConditionTrue,
|
||||||
},
|
},
|
||||||
|
@ -89,10 +89,10 @@ func makePod() *v1.Pod {
|
||||||
Spec: v1.PodSpec{
|
Spec: v1.PodSpec{
|
||||||
NodeName: "testnode",
|
NodeName: "testnode",
|
||||||
Containers: []v1.Container{
|
Containers: []v1.Container{
|
||||||
v1.Container{
|
{
|
||||||
Name: "testcontainer",
|
Name: "testcontainer",
|
||||||
Ports: []v1.ContainerPort{
|
Ports: []v1.ContainerPort{
|
||||||
v1.ContainerPort{
|
{
|
||||||
Name: "testport",
|
Name: "testport",
|
||||||
Protocol: v1.ProtocolTCP,
|
Protocol: v1.ProtocolTCP,
|
||||||
ContainerPort: int32(9000),
|
ContainerPort: int32(9000),
|
||||||
|
@ -105,7 +105,7 @@ func makePod() *v1.Pod {
|
||||||
PodIP: "1.2.3.4",
|
PodIP: "1.2.3.4",
|
||||||
HostIP: "2.3.4.5",
|
HostIP: "2.3.4.5",
|
||||||
Conditions: []v1.PodCondition{
|
Conditions: []v1.PodCondition{
|
||||||
v1.PodCondition{
|
{
|
||||||
Type: v1.PodReady,
|
Type: v1.PodReady,
|
||||||
Status: v1.ConditionTrue,
|
Status: v1.ConditionTrue,
|
||||||
},
|
},
|
||||||
|
@ -121,23 +121,23 @@ func TestPodDiscoveryInitial(t *testing.T) {
|
||||||
k8sDiscoveryTest{
|
k8sDiscoveryTest{
|
||||||
discovery: n,
|
discovery: n,
|
||||||
expectedInitial: []*config.TargetGroup{
|
expectedInitial: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Targets: []model.LabelSet{
|
Targets: []model.LabelSet{
|
||||||
model.LabelSet{
|
{
|
||||||
"__address__": "1.2.3.4:9000",
|
"__address__": "1.2.3.4:9000",
|
||||||
"__meta_kubernetes_pod_container_name": "testcontainer0",
|
"__meta_kubernetes_pod_container_name": "testcontainer0",
|
||||||
"__meta_kubernetes_pod_container_port_name": "testport0",
|
"__meta_kubernetes_pod_container_port_name": "testport0",
|
||||||
"__meta_kubernetes_pod_container_port_number": "9000",
|
"__meta_kubernetes_pod_container_port_number": "9000",
|
||||||
"__meta_kubernetes_pod_container_port_protocol": "TCP",
|
"__meta_kubernetes_pod_container_port_protocol": "TCP",
|
||||||
},
|
},
|
||||||
model.LabelSet{
|
{
|
||||||
"__address__": "1.2.3.4:9001",
|
"__address__": "1.2.3.4:9001",
|
||||||
"__meta_kubernetes_pod_container_name": "testcontainer0",
|
"__meta_kubernetes_pod_container_name": "testcontainer0",
|
||||||
"__meta_kubernetes_pod_container_port_name": "testport1",
|
"__meta_kubernetes_pod_container_port_name": "testport1",
|
||||||
"__meta_kubernetes_pod_container_port_number": "9001",
|
"__meta_kubernetes_pod_container_port_number": "9001",
|
||||||
"__meta_kubernetes_pod_container_port_protocol": "UDP",
|
"__meta_kubernetes_pod_container_port_protocol": "UDP",
|
||||||
},
|
},
|
||||||
model.LabelSet{
|
{
|
||||||
"__address__": "1.2.3.4",
|
"__address__": "1.2.3.4",
|
||||||
"__meta_kubernetes_pod_container_name": "testcontainer1",
|
"__meta_kubernetes_pod_container_name": "testcontainer1",
|
||||||
},
|
},
|
||||||
|
@ -165,9 +165,9 @@ func TestPodDiscoveryAdd(t *testing.T) {
|
||||||
discovery: n,
|
discovery: n,
|
||||||
afterStart: func() { go func() { i.Add(makePod()) }() },
|
afterStart: func() { go func() { i.Add(makePod()) }() },
|
||||||
expectedRes: []*config.TargetGroup{
|
expectedRes: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Targets: []model.LabelSet{
|
Targets: []model.LabelSet{
|
||||||
model.LabelSet{
|
{
|
||||||
"__address__": "1.2.3.4:9000",
|
"__address__": "1.2.3.4:9000",
|
||||||
"__meta_kubernetes_pod_container_name": "testcontainer",
|
"__meta_kubernetes_pod_container_name": "testcontainer",
|
||||||
"__meta_kubernetes_pod_container_port_name": "testport",
|
"__meta_kubernetes_pod_container_port_name": "testport",
|
||||||
|
@ -197,9 +197,9 @@ func TestPodDiscoveryDelete(t *testing.T) {
|
||||||
discovery: n,
|
discovery: n,
|
||||||
afterStart: func() { go func() { i.Delete(makePod()) }() },
|
afterStart: func() { go func() { i.Delete(makePod()) }() },
|
||||||
expectedInitial: []*config.TargetGroup{
|
expectedInitial: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Targets: []model.LabelSet{
|
Targets: []model.LabelSet{
|
||||||
model.LabelSet{
|
{
|
||||||
"__address__": "1.2.3.4:9000",
|
"__address__": "1.2.3.4:9000",
|
||||||
"__meta_kubernetes_pod_container_name": "testcontainer",
|
"__meta_kubernetes_pod_container_name": "testcontainer",
|
||||||
"__meta_kubernetes_pod_container_port_name": "testport",
|
"__meta_kubernetes_pod_container_port_name": "testport",
|
||||||
|
@ -219,7 +219,7 @@ func TestPodDiscoveryDelete(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedRes: []*config.TargetGroup{
|
expectedRes: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Source: "pod/default/testpod",
|
Source: "pod/default/testpod",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -234,9 +234,9 @@ func TestPodDiscoveryDeleteUnknownCacheState(t *testing.T) {
|
||||||
discovery: n,
|
discovery: n,
|
||||||
afterStart: func() { go func() { i.Delete(cache.DeletedFinalStateUnknown{Obj: makePod()}) }() },
|
afterStart: func() { go func() { i.Delete(cache.DeletedFinalStateUnknown{Obj: makePod()}) }() },
|
||||||
expectedInitial: []*config.TargetGroup{
|
expectedInitial: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Targets: []model.LabelSet{
|
Targets: []model.LabelSet{
|
||||||
model.LabelSet{
|
{
|
||||||
"__address__": "1.2.3.4:9000",
|
"__address__": "1.2.3.4:9000",
|
||||||
"__meta_kubernetes_pod_container_name": "testcontainer",
|
"__meta_kubernetes_pod_container_name": "testcontainer",
|
||||||
"__meta_kubernetes_pod_container_port_name": "testport",
|
"__meta_kubernetes_pod_container_port_name": "testport",
|
||||||
|
@ -256,7 +256,7 @@ func TestPodDiscoveryDeleteUnknownCacheState(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedRes: []*config.TargetGroup{
|
expectedRes: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Source: "pod/default/testpod",
|
Source: "pod/default/testpod",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -273,10 +273,10 @@ func TestPodDiscoveryUpdate(t *testing.T) {
|
||||||
Spec: v1.PodSpec{
|
Spec: v1.PodSpec{
|
||||||
NodeName: "testnode",
|
NodeName: "testnode",
|
||||||
Containers: []v1.Container{
|
Containers: []v1.Container{
|
||||||
v1.Container{
|
{
|
||||||
Name: "testcontainer",
|
Name: "testcontainer",
|
||||||
Ports: []v1.ContainerPort{
|
Ports: []v1.ContainerPort{
|
||||||
v1.ContainerPort{
|
{
|
||||||
Name: "testport",
|
Name: "testport",
|
||||||
Protocol: v1.ProtocolTCP,
|
Protocol: v1.ProtocolTCP,
|
||||||
ContainerPort: int32(9000),
|
ContainerPort: int32(9000),
|
||||||
|
@ -295,9 +295,9 @@ func TestPodDiscoveryUpdate(t *testing.T) {
|
||||||
discovery: n,
|
discovery: n,
|
||||||
afterStart: func() { go func() { i.Update(makePod()) }() },
|
afterStart: func() { go func() { i.Update(makePod()) }() },
|
||||||
expectedInitial: []*config.TargetGroup{
|
expectedInitial: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Targets: []model.LabelSet{
|
Targets: []model.LabelSet{
|
||||||
model.LabelSet{
|
{
|
||||||
"__address__": "1.2.3.4:9000",
|
"__address__": "1.2.3.4:9000",
|
||||||
"__meta_kubernetes_pod_container_name": "testcontainer",
|
"__meta_kubernetes_pod_container_name": "testcontainer",
|
||||||
"__meta_kubernetes_pod_container_port_name": "testport",
|
"__meta_kubernetes_pod_container_port_name": "testport",
|
||||||
|
@ -317,9 +317,9 @@ func TestPodDiscoveryUpdate(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedRes: []*config.TargetGroup{
|
expectedRes: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Targets: []model.LabelSet{
|
Targets: []model.LabelSet{
|
||||||
model.LabelSet{
|
{
|
||||||
"__address__": "1.2.3.4:9000",
|
"__address__": "1.2.3.4:9000",
|
||||||
"__meta_kubernetes_pod_container_name": "testcontainer",
|
"__meta_kubernetes_pod_container_name": "testcontainer",
|
||||||
"__meta_kubernetes_pod_container_port_name": "testport",
|
"__meta_kubernetes_pod_container_port_name": "testport",
|
||||||
|
|
|
@ -47,12 +47,12 @@ func makeMultiPortService() *v1.Service {
|
||||||
},
|
},
|
||||||
Spec: v1.ServiceSpec{
|
Spec: v1.ServiceSpec{
|
||||||
Ports: []v1.ServicePort{
|
Ports: []v1.ServicePort{
|
||||||
v1.ServicePort{
|
{
|
||||||
Name: "testport0",
|
Name: "testport0",
|
||||||
Protocol: v1.ProtocolTCP,
|
Protocol: v1.ProtocolTCP,
|
||||||
Port: int32(30900),
|
Port: int32(30900),
|
||||||
},
|
},
|
||||||
v1.ServicePort{
|
{
|
||||||
Name: "testport1",
|
Name: "testport1",
|
||||||
Protocol: v1.ProtocolUDP,
|
Protocol: v1.ProtocolUDP,
|
||||||
Port: int32(30901),
|
Port: int32(30901),
|
||||||
|
@ -70,7 +70,7 @@ func makeSuffixedService(suffix string) *v1.Service {
|
||||||
},
|
},
|
||||||
Spec: v1.ServiceSpec{
|
Spec: v1.ServiceSpec{
|
||||||
Ports: []v1.ServicePort{
|
Ports: []v1.ServicePort{
|
||||||
v1.ServicePort{
|
{
|
||||||
Name: "testport",
|
Name: "testport",
|
||||||
Protocol: v1.ProtocolTCP,
|
Protocol: v1.ProtocolTCP,
|
||||||
Port: int32(30900),
|
Port: int32(30900),
|
||||||
|
@ -91,14 +91,14 @@ func TestServiceDiscoveryInitial(t *testing.T) {
|
||||||
k8sDiscoveryTest{
|
k8sDiscoveryTest{
|
||||||
discovery: n,
|
discovery: n,
|
||||||
expectedInitial: []*config.TargetGroup{
|
expectedInitial: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Targets: []model.LabelSet{
|
Targets: []model.LabelSet{
|
||||||
model.LabelSet{
|
{
|
||||||
"__meta_kubernetes_service_port_protocol": "TCP",
|
"__meta_kubernetes_service_port_protocol": "TCP",
|
||||||
"__address__": "testservice.default.svc:30900",
|
"__address__": "testservice.default.svc:30900",
|
||||||
"__meta_kubernetes_service_port_name": "testport0",
|
"__meta_kubernetes_service_port_name": "testport0",
|
||||||
},
|
},
|
||||||
model.LabelSet{
|
{
|
||||||
"__meta_kubernetes_service_port_protocol": "UDP",
|
"__meta_kubernetes_service_port_protocol": "UDP",
|
||||||
"__address__": "testservice.default.svc:30901",
|
"__address__": "testservice.default.svc:30901",
|
||||||
"__meta_kubernetes_service_port_name": "testport1",
|
"__meta_kubernetes_service_port_name": "testport1",
|
||||||
|
@ -123,9 +123,9 @@ func TestServiceDiscoveryAdd(t *testing.T) {
|
||||||
discovery: n,
|
discovery: n,
|
||||||
afterStart: func() { go func() { i.Add(makeService()) }() },
|
afterStart: func() { go func() { i.Add(makeService()) }() },
|
||||||
expectedRes: []*config.TargetGroup{
|
expectedRes: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Targets: []model.LabelSet{
|
Targets: []model.LabelSet{
|
||||||
model.LabelSet{
|
{
|
||||||
"__meta_kubernetes_service_port_protocol": "TCP",
|
"__meta_kubernetes_service_port_protocol": "TCP",
|
||||||
"__address__": "testservice.default.svc:30900",
|
"__address__": "testservice.default.svc:30900",
|
||||||
"__meta_kubernetes_service_port_name": "testport",
|
"__meta_kubernetes_service_port_name": "testport",
|
||||||
|
@ -149,9 +149,9 @@ func TestServiceDiscoveryDelete(t *testing.T) {
|
||||||
discovery: n,
|
discovery: n,
|
||||||
afterStart: func() { go func() { i.Delete(makeService()) }() },
|
afterStart: func() { go func() { i.Delete(makeService()) }() },
|
||||||
expectedInitial: []*config.TargetGroup{
|
expectedInitial: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Targets: []model.LabelSet{
|
Targets: []model.LabelSet{
|
||||||
model.LabelSet{
|
{
|
||||||
"__meta_kubernetes_service_port_protocol": "TCP",
|
"__meta_kubernetes_service_port_protocol": "TCP",
|
||||||
"__address__": "testservice.default.svc:30900",
|
"__address__": "testservice.default.svc:30900",
|
||||||
"__meta_kubernetes_service_port_name": "testport",
|
"__meta_kubernetes_service_port_name": "testport",
|
||||||
|
@ -165,7 +165,7 @@ func TestServiceDiscoveryDelete(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedRes: []*config.TargetGroup{
|
expectedRes: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Source: "svc/default/testservice",
|
Source: "svc/default/testservice",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -180,9 +180,9 @@ func TestServiceDiscoveryDeleteUnknownCacheState(t *testing.T) {
|
||||||
discovery: n,
|
discovery: n,
|
||||||
afterStart: func() { go func() { i.Delete(cache.DeletedFinalStateUnknown{Obj: makeService()}) }() },
|
afterStart: func() { go func() { i.Delete(cache.DeletedFinalStateUnknown{Obj: makeService()}) }() },
|
||||||
expectedInitial: []*config.TargetGroup{
|
expectedInitial: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Targets: []model.LabelSet{
|
Targets: []model.LabelSet{
|
||||||
model.LabelSet{
|
{
|
||||||
"__meta_kubernetes_service_port_protocol": "TCP",
|
"__meta_kubernetes_service_port_protocol": "TCP",
|
||||||
"__address__": "testservice.default.svc:30900",
|
"__address__": "testservice.default.svc:30900",
|
||||||
"__meta_kubernetes_service_port_name": "testport",
|
"__meta_kubernetes_service_port_name": "testport",
|
||||||
|
@ -196,7 +196,7 @@ func TestServiceDiscoveryDeleteUnknownCacheState(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedRes: []*config.TargetGroup{
|
expectedRes: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Source: "svc/default/testservice",
|
Source: "svc/default/testservice",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -211,9 +211,9 @@ func TestServiceDiscoveryUpdate(t *testing.T) {
|
||||||
discovery: n,
|
discovery: n,
|
||||||
afterStart: func() { go func() { i.Update(makeMultiPortService()) }() },
|
afterStart: func() { go func() { i.Update(makeMultiPortService()) }() },
|
||||||
expectedInitial: []*config.TargetGroup{
|
expectedInitial: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Targets: []model.LabelSet{
|
Targets: []model.LabelSet{
|
||||||
model.LabelSet{
|
{
|
||||||
"__meta_kubernetes_service_port_protocol": "TCP",
|
"__meta_kubernetes_service_port_protocol": "TCP",
|
||||||
"__address__": "testservice.default.svc:30900",
|
"__address__": "testservice.default.svc:30900",
|
||||||
"__meta_kubernetes_service_port_name": "testport",
|
"__meta_kubernetes_service_port_name": "testport",
|
||||||
|
@ -227,14 +227,14 @@ func TestServiceDiscoveryUpdate(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedRes: []*config.TargetGroup{
|
expectedRes: []*config.TargetGroup{
|
||||||
&config.TargetGroup{
|
{
|
||||||
Targets: []model.LabelSet{
|
Targets: []model.LabelSet{
|
||||||
model.LabelSet{
|
{
|
||||||
"__meta_kubernetes_service_port_protocol": "TCP",
|
"__meta_kubernetes_service_port_protocol": "TCP",
|
||||||
"__address__": "testservice.default.svc:30900",
|
"__address__": "testservice.default.svc:30900",
|
||||||
"__meta_kubernetes_service_port_name": "testport0",
|
"__meta_kubernetes_service_port_name": "testport0",
|
||||||
},
|
},
|
||||||
model.LabelSet{
|
{
|
||||||
"__meta_kubernetes_service_port_protocol": "UDP",
|
"__meta_kubernetes_service_port_protocol": "UDP",
|
||||||
"__address__": "testservice.default.svc:30901",
|
"__address__": "testservice.default.svc:30901",
|
||||||
"__meta_kubernetes_service_port_name": "testport1",
|
"__meta_kubernetes_service_port_name": "testport1",
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
@ -77,15 +78,25 @@ type Discovery struct {
|
||||||
refreshInterval time.Duration
|
refreshInterval time.Duration
|
||||||
lastRefresh map[string]*config.TargetGroup
|
lastRefresh map[string]*config.TargetGroup
|
||||||
appsClient AppListClient
|
appsClient AppListClient
|
||||||
|
token string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize sets up the discovery for usage.
|
// NewDiscovery returns a new Marathon Discovery.
|
||||||
func NewDiscovery(conf *config.MarathonSDConfig) (*Discovery, error) {
|
func NewDiscovery(conf *config.MarathonSDConfig) (*Discovery, error) {
|
||||||
tls, err := httputil.NewTLSConfig(conf.TLSConfig)
|
tls, err := httputil.NewTLSConfig(conf.TLSConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
token := conf.BearerToken
|
||||||
|
if conf.BearerTokenFile != "" {
|
||||||
|
bf, err := ioutil.ReadFile(conf.BearerTokenFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
token = strings.TrimSpace(string(bf))
|
||||||
|
}
|
||||||
|
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
Timeout: time.Duration(conf.Timeout),
|
Timeout: time.Duration(conf.Timeout),
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
|
@ -98,17 +109,18 @@ func NewDiscovery(conf *config.MarathonSDConfig) (*Discovery, error) {
|
||||||
servers: conf.Servers,
|
servers: conf.Servers,
|
||||||
refreshInterval: time.Duration(conf.RefreshInterval),
|
refreshInterval: time.Duration(conf.RefreshInterval),
|
||||||
appsClient: fetchApps,
|
appsClient: fetchApps,
|
||||||
|
token: token,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run implements the TargetProvider interface.
|
// Run implements the TargetProvider interface.
|
||||||
func (md *Discovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
func (d *Discovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
case <-time.After(md.refreshInterval):
|
case <-time.After(d.refreshInterval):
|
||||||
err := md.updateServices(ctx, ch)
|
err := d.updateServices(ctx, ch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error while updating services: %s", err)
|
log.Errorf("Error while updating services: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -116,7 +128,7 @@ func (md *Discovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (md *Discovery) updateServices(ctx context.Context, ch chan<- []*config.TargetGroup) (err error) {
|
func (d *Discovery) updateServices(ctx context.Context, ch chan<- []*config.TargetGroup) (err error) {
|
||||||
t0 := time.Now()
|
t0 := time.Now()
|
||||||
defer func() {
|
defer func() {
|
||||||
refreshDuration.Observe(time.Since(t0).Seconds())
|
refreshDuration.Observe(time.Since(t0).Seconds())
|
||||||
|
@ -125,7 +137,7 @@ func (md *Discovery) updateServices(ctx context.Context, ch chan<- []*config.Tar
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
targetMap, err := md.fetchTargetGroups()
|
targetMap, err := d.fetchTargetGroups()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -142,7 +154,7 @@ func (md *Discovery) updateServices(ctx context.Context, ch chan<- []*config.Tar
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove services which did disappear.
|
// Remove services which did disappear.
|
||||||
for source := range md.lastRefresh {
|
for source := range d.lastRefresh {
|
||||||
_, ok := targetMap[source]
|
_, ok := targetMap[source]
|
||||||
if !ok {
|
if !ok {
|
||||||
select {
|
select {
|
||||||
|
@ -154,13 +166,13 @@ func (md *Discovery) updateServices(ctx context.Context, ch chan<- []*config.Tar
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
md.lastRefresh = targetMap
|
d.lastRefresh = targetMap
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (md *Discovery) fetchTargetGroups() (map[string]*config.TargetGroup, error) {
|
func (d *Discovery) fetchTargetGroups() (map[string]*config.TargetGroup, error) {
|
||||||
url := RandomAppsURL(md.servers)
|
url := RandomAppsURL(d.servers)
|
||||||
apps, err := md.appsClient(md.client, url)
|
apps, err := d.appsClient(d.client, url, d.token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -201,11 +213,23 @@ type AppList struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppListClient defines a function that can be used to get an application list from marathon.
|
// AppListClient defines a function that can be used to get an application list from marathon.
|
||||||
type AppListClient func(client *http.Client, url string) (*AppList, error)
|
type AppListClient func(client *http.Client, url, token string) (*AppList, error)
|
||||||
|
|
||||||
// fetchApps requests a list of applications from a marathon server.
|
// fetchApps requests a list of applications from a marathon server.
|
||||||
func fetchApps(client *http.Client, url string) (*AppList, error) {
|
func fetchApps(client *http.Client, url, token string) (*AppList, error) {
|
||||||
resp, err := client.Get(url)
|
request, err := http.NewRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// According to https://dcos.io/docs/1.8/administration/id-and-access-mgt/managing-authentication
|
||||||
|
// DC/OS wants with "token=" a different Authorization header than implemented in httputil/client.go
|
||||||
|
// so we set this implicitly here
|
||||||
|
if token != "" {
|
||||||
|
request.Header.Set("Authorization", "token="+token)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Do(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ func TestMarathonSDHandleError(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
errTesting = errors.New("testing failure")
|
errTesting = errors.New("testing failure")
|
||||||
ch = make(chan []*config.TargetGroup, 1)
|
ch = make(chan []*config.TargetGroup, 1)
|
||||||
client = func(client *http.Client, url string) (*AppList, error) { return nil, errTesting }
|
client = func(client *http.Client, url, token string) (*AppList, error) { return nil, errTesting }
|
||||||
)
|
)
|
||||||
if err := testUpdateServices(client, ch); err != errTesting {
|
if err := testUpdateServices(client, ch); err != errTesting {
|
||||||
t.Fatalf("Expected error: %s", err)
|
t.Fatalf("Expected error: %s", err)
|
||||||
|
@ -59,7 +59,7 @@ func TestMarathonSDHandleError(t *testing.T) {
|
||||||
func TestMarathonSDEmptyList(t *testing.T) {
|
func TestMarathonSDEmptyList(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
ch = make(chan []*config.TargetGroup, 1)
|
ch = make(chan []*config.TargetGroup, 1)
|
||||||
client = func(client *http.Client, url string) (*AppList, error) { return &AppList{}, nil }
|
client = func(client *http.Client, url, token string) (*AppList, error) { return &AppList{}, nil }
|
||||||
)
|
)
|
||||||
if err := testUpdateServices(client, ch); err != nil {
|
if err := testUpdateServices(client, ch); err != nil {
|
||||||
t.Fatalf("Got error: %s", err)
|
t.Fatalf("Got error: %s", err)
|
||||||
|
@ -98,7 +98,7 @@ func marathonTestAppList(labels map[string]string, runningTasks int) *AppList {
|
||||||
func TestMarathonSDSendGroup(t *testing.T) {
|
func TestMarathonSDSendGroup(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
ch = make(chan []*config.TargetGroup, 1)
|
ch = make(chan []*config.TargetGroup, 1)
|
||||||
client = func(client *http.Client, url string) (*AppList, error) {
|
client = func(client *http.Client, url, token string) (*AppList, error) {
|
||||||
return marathonTestAppList(marathonValidLabel, 1), nil
|
return marathonTestAppList(marathonValidLabel, 1), nil
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -125,34 +125,34 @@ func TestMarathonSDSendGroup(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMarathonSDRemoveApp(t *testing.T) {
|
func TestMarathonSDRemoveApp(t *testing.T) {
|
||||||
var ch = make(chan []*config.TargetGroup)
|
var ch = make(chan []*config.TargetGroup, 1)
|
||||||
md, err := NewDiscovery(&conf)
|
md, err := NewDiscovery(&conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("%s", err)
|
t.Fatalf("%s", err)
|
||||||
}
|
}
|
||||||
md.appsClient = func(client *http.Client, url string) (*AppList, error) {
|
|
||||||
|
md.appsClient = func(client *http.Client, url, token string) (*AppList, error) {
|
||||||
return marathonTestAppList(marathonValidLabel, 1), nil
|
return marathonTestAppList(marathonValidLabel, 1), nil
|
||||||
}
|
}
|
||||||
go func() {
|
if err := md.updateServices(context.Background(), ch); err != nil {
|
||||||
|
t.Fatalf("Got error on first update: %s", err)
|
||||||
|
}
|
||||||
up1 := (<-ch)[0]
|
up1 := (<-ch)[0]
|
||||||
|
|
||||||
|
md.appsClient = func(client *http.Client, url, token string) (*AppList, error) {
|
||||||
|
return marathonTestAppList(marathonValidLabel, 0), nil
|
||||||
|
}
|
||||||
|
if err := md.updateServices(context.Background(), ch); err != nil {
|
||||||
|
t.Fatalf("Got error on second update: %s", err)
|
||||||
|
}
|
||||||
up2 := (<-ch)[0]
|
up2 := (<-ch)[0]
|
||||||
|
|
||||||
if up2.Source != up1.Source {
|
if up2.Source != up1.Source {
|
||||||
t.Fatalf("Source is different: %s", up2)
|
t.Fatalf("Source is different: %s", up2)
|
||||||
if len(up2.Targets) > 0 {
|
if len(up2.Targets) > 0 {
|
||||||
t.Fatalf("Got a non-empty target set: %s", up2.Targets)
|
t.Fatalf("Got a non-empty target set: %s", up2.Targets)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
if err := md.updateServices(context.Background(), ch); err != nil {
|
|
||||||
t.Fatalf("Got error on first update: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
md.appsClient = func(client *http.Client, url string) (*AppList, error) {
|
|
||||||
return marathonTestAppList(marathonValidLabel, 0), nil
|
|
||||||
}
|
|
||||||
if err := md.updateServices(context.Background(), ch); err != nil {
|
|
||||||
t.Fatalf("Got error on second update: %s", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMarathonSDRunAndStop(t *testing.T) {
|
func TestMarathonSDRunAndStop(t *testing.T) {
|
||||||
|
@ -160,32 +160,33 @@ func TestMarathonSDRunAndStop(t *testing.T) {
|
||||||
refreshInterval = model.Duration(time.Millisecond * 10)
|
refreshInterval = model.Duration(time.Millisecond * 10)
|
||||||
conf = config.MarathonSDConfig{Servers: testServers, RefreshInterval: refreshInterval}
|
conf = config.MarathonSDConfig{Servers: testServers, RefreshInterval: refreshInterval}
|
||||||
ch = make(chan []*config.TargetGroup)
|
ch = make(chan []*config.TargetGroup)
|
||||||
|
doneCh = make(chan error)
|
||||||
)
|
)
|
||||||
md, err := NewDiscovery(&conf)
|
md, err := NewDiscovery(&conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("%s", err)
|
t.Fatalf("%s", err)
|
||||||
}
|
}
|
||||||
md.appsClient = func(client *http.Client, url string) (*AppList, error) {
|
md.appsClient = func(client *http.Client, url, token string) (*AppList, error) {
|
||||||
return marathonTestAppList(marathonValidLabel, 1), nil
|
return marathonTestAppList(marathonValidLabel, 1), nil
|
||||||
}
|
}
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
md.Run(ctx, ch)
|
||||||
|
close(doneCh)
|
||||||
|
}()
|
||||||
|
|
||||||
|
timeout := time.After(md.refreshInterval * 3)
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case _, ok := <-ch:
|
case <-ch:
|
||||||
if !ok {
|
cancel()
|
||||||
|
case <-doneCh:
|
||||||
return
|
return
|
||||||
}
|
case <-timeout:
|
||||||
cancel()
|
|
||||||
case <-time.After(md.refreshInterval * 3):
|
|
||||||
cancel()
|
|
||||||
t.Fatalf("Update took too long.")
|
t.Fatalf("Update took too long.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
|
|
||||||
md.Run(ctx, ch)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func marathonTestZeroTaskPortAppList(labels map[string]string, runningTasks int) *AppList {
|
func marathonTestZeroTaskPortAppList(labels map[string]string, runningTasks int) *AppList {
|
||||||
|
@ -213,7 +214,7 @@ func marathonTestZeroTaskPortAppList(labels map[string]string, runningTasks int)
|
||||||
func TestMarathonZeroTaskPorts(t *testing.T) {
|
func TestMarathonZeroTaskPorts(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
ch = make(chan []*config.TargetGroup, 1)
|
ch = make(chan []*config.TargetGroup, 1)
|
||||||
client = func(client *http.Client, url string) (*AppList, error) {
|
client = func(client *http.Client, url, token string) (*AppList, error) {
|
||||||
return marathonTestZeroTaskPortAppList(marathonValidLabel, 1), nil
|
return marathonTestZeroTaskPortAppList(marathonValidLabel, 1), nil
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -29,7 +29,9 @@ import (
|
||||||
"github.com/prometheus/prometheus/util/treecache"
|
"github.com/prometheus/prometheus/util/treecache"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ZookeeperDiscovery struct {
|
// Discovery implements the TargetProvider interface for discovering
|
||||||
|
// targets from Zookeeper.
|
||||||
|
type Discovery struct {
|
||||||
conn *zk.Conn
|
conn *zk.Conn
|
||||||
|
|
||||||
sources map[string]*config.TargetGroup
|
sources map[string]*config.TargetGroup
|
||||||
|
@ -40,13 +42,13 @@ type ZookeeperDiscovery struct {
|
||||||
parse func(data []byte, path string) (model.LabelSet, error)
|
parse func(data []byte, path string) (model.LabelSet, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewNerveDiscovery returns a new NerveDiscovery for the given config.
|
// NewNerveDiscovery returns a new Discovery for the given Nerve config.
|
||||||
func NewNerveDiscovery(conf *config.NerveSDConfig) *ZookeeperDiscovery {
|
func NewNerveDiscovery(conf *config.NerveSDConfig) *Discovery {
|
||||||
return NewDiscovery(conf.Servers, time.Duration(conf.Timeout), conf.Paths, parseNerveMember)
|
return NewDiscovery(conf.Servers, time.Duration(conf.Timeout), conf.Paths, parseNerveMember)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServersetDiscovery returns a new ServersetDiscovery for the given config.
|
// NewServersetDiscovery returns a new Discovery for the given serverset config.
|
||||||
func NewServersetDiscovery(conf *config.ServersetSDConfig) *ZookeeperDiscovery {
|
func NewServersetDiscovery(conf *config.ServersetSDConfig) *Discovery {
|
||||||
return NewDiscovery(conf.Servers, time.Duration(conf.Timeout), conf.Paths, parseServersetMember)
|
return NewDiscovery(conf.Servers, time.Duration(conf.Timeout), conf.Paths, parseServersetMember)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,14 +59,14 @@ func NewDiscovery(
|
||||||
timeout time.Duration,
|
timeout time.Duration,
|
||||||
paths []string,
|
paths []string,
|
||||||
pf func(data []byte, path string) (model.LabelSet, error),
|
pf func(data []byte, path string) (model.LabelSet, error),
|
||||||
) *ZookeeperDiscovery {
|
) *Discovery {
|
||||||
conn, _, err := zk.Connect(srvs, time.Duration(timeout))
|
conn, _, err := zk.Connect(srvs, timeout)
|
||||||
conn.SetLogger(treecache.ZookeeperLogger{})
|
conn.SetLogger(treecache.ZookeeperLogger{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
updates := make(chan treecache.ZookeeperTreeCacheEvent)
|
updates := make(chan treecache.ZookeeperTreeCacheEvent)
|
||||||
sd := &ZookeeperDiscovery{
|
sd := &Discovery{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
updates: updates,
|
updates: updates,
|
||||||
sources: map[string]*config.TargetGroup{},
|
sources: map[string]*config.TargetGroup{},
|
||||||
|
@ -77,34 +79,34 @@ func NewDiscovery(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run implements the TargetProvider interface.
|
// Run implements the TargetProvider interface.
|
||||||
func (sd *ZookeeperDiscovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
func (d *Discovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup) {
|
||||||
defer func() {
|
defer func() {
|
||||||
for _, tc := range sd.treeCaches {
|
for _, tc := range d.treeCaches {
|
||||||
tc.Stop()
|
tc.Stop()
|
||||||
}
|
}
|
||||||
// Drain event channel in case the treecache leaks goroutines otherwise.
|
// Drain event channel in case the treecache leaks goroutines otherwise.
|
||||||
for range sd.updates {
|
for range d.updates {
|
||||||
}
|
}
|
||||||
sd.conn.Close()
|
d.conn.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
case event := <-sd.updates:
|
case event := <-d.updates:
|
||||||
tg := &config.TargetGroup{
|
tg := &config.TargetGroup{
|
||||||
Source: event.Path,
|
Source: event.Path,
|
||||||
}
|
}
|
||||||
if event.Data != nil {
|
if event.Data != nil {
|
||||||
labelSet, err := sd.parse(*event.Data, event.Path)
|
labelSet, err := d.parse(*event.Data, event.Path)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
tg.Targets = []model.LabelSet{labelSet}
|
tg.Targets = []model.LabelSet{labelSet}
|
||||||
sd.sources[event.Path] = tg
|
d.sources[event.Path] = tg
|
||||||
} else {
|
} else {
|
||||||
delete(sd.sources, event.Path)
|
delete(d.sources, event.Path)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
delete(sd.sources, event.Path)
|
delete(d.sources, event.Path)
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
|
|
@ -61,7 +61,7 @@ var (
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Subsystem: subsystem,
|
Subsystem: subsystem,
|
||||||
Name: "query_duration_seconds",
|
Name: "query_duration_seconds",
|
||||||
Help: "Query timmings",
|
Help: "Query timings",
|
||||||
ConstLabels: prometheus.Labels{"slice": "prepare_time"},
|
ConstLabels: prometheus.Labels{"slice": "prepare_time"},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -70,7 +70,7 @@ var (
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Subsystem: subsystem,
|
Subsystem: subsystem,
|
||||||
Name: "query_duration_seconds",
|
Name: "query_duration_seconds",
|
||||||
Help: "Query timmings",
|
Help: "Query timings",
|
||||||
ConstLabels: prometheus.Labels{"slice": "inner_eval"},
|
ConstLabels: prometheus.Labels{"slice": "inner_eval"},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -79,7 +79,7 @@ var (
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Subsystem: subsystem,
|
Subsystem: subsystem,
|
||||||
Name: "query_duration_seconds",
|
Name: "query_duration_seconds",
|
||||||
Help: "Query timmings",
|
Help: "Query timings",
|
||||||
ConstLabels: prometheus.Labels{"slice": "result_append"},
|
ConstLabels: prometheus.Labels{"slice": "result_append"},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -88,7 +88,7 @@ var (
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Subsystem: subsystem,
|
Subsystem: subsystem,
|
||||||
Name: "query_duration_seconds",
|
Name: "query_duration_seconds",
|
||||||
Help: "Query timmings",
|
Help: "Query timings",
|
||||||
ConstLabels: prometheus.Labels{"slice": "result_sort"},
|
ConstLabels: prometheus.Labels{"slice": "result_sort"},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -26,6 +26,8 @@ import (
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/common/log"
|
"github.com/prometheus/common/log"
|
||||||
|
|
||||||
|
"github.com/prometheus/common/version"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"golang.org/x/net/context/ctxhttp"
|
"golang.org/x/net/context/ctxhttp"
|
||||||
|
|
||||||
|
@ -360,6 +362,8 @@ type targetScraper struct {
|
||||||
|
|
||||||
const acceptHeader = `application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;q=0.7,text/plain;version=0.0.4;q=0.3,*/*;q=0.1`
|
const acceptHeader = `application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;q=0.7,text/plain;version=0.0.4;q=0.3,*/*;q=0.1`
|
||||||
|
|
||||||
|
var userAgentHeader = fmt.Sprintf("Prometheus/%s", version.Version)
|
||||||
|
|
||||||
func (s *targetScraper) scrape(ctx context.Context, w io.Writer) error {
|
func (s *targetScraper) scrape(ctx context.Context, w io.Writer) error {
|
||||||
if s.req == nil {
|
if s.req == nil {
|
||||||
req, err := http.NewRequest("GET", s.URL().String(), nil)
|
req, err := http.NewRequest("GET", s.URL().String(), nil)
|
||||||
|
@ -369,6 +373,7 @@ func (s *targetScraper) scrape(ctx context.Context, w io.Writer) error {
|
||||||
// Disable accept header to always negotiate for text format.
|
// Disable accept header to always negotiate for text format.
|
||||||
// req.Header.Add("Accept", acceptHeader)
|
// req.Header.Add("Accept", acceptHeader)
|
||||||
req.Header.Add("Accept-Encoding", "gzip")
|
req.Header.Add("Accept-Encoding", "gzip")
|
||||||
|
req.Header.Set("User-Agent", userAgentHeader)
|
||||||
|
|
||||||
s.req = req
|
s.req = req
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,11 +86,7 @@ func NewZookeeperTreeCache(conn *zk.Conn, path string, events chan ZookeeperTree
|
||||||
children: map[string]*zookeeperTreeCacheNode{},
|
children: map[string]*zookeeperTreeCacheNode{},
|
||||||
stopped: true,
|
stopped: true,
|
||||||
}
|
}
|
||||||
err := tc.recursiveNodeUpdate(path, tc.head)
|
go tc.loop(path)
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Error during initial read of Zookeeper: %s", err)
|
|
||||||
}
|
|
||||||
go tc.loop(err != nil)
|
|
||||||
return tc
|
return tc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +94,8 @@ func (tc *ZookeeperTreeCache) Stop() {
|
||||||
tc.stop <- struct{}{}
|
tc.stop <- struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tc *ZookeeperTreeCache) loop(failureMode bool) {
|
func (tc *ZookeeperTreeCache) loop(path string) {
|
||||||
|
failureMode := false
|
||||||
retryChan := make(chan struct{})
|
retryChan := make(chan struct{})
|
||||||
|
|
||||||
failure := func() {
|
failure := func() {
|
||||||
|
@ -108,7 +105,10 @@ func (tc *ZookeeperTreeCache) loop(failureMode bool) {
|
||||||
retryChan <- struct{}{}
|
retryChan <- struct{}{}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if failureMode {
|
|
||||||
|
err := tc.recursiveNodeUpdate(path, tc.head)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Error during initial read of Zookeeper: %s", err)
|
||||||
failure()
|
failure()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -170,12 +170,24 @@ func (api *API) query(r *http.Request) (interface{}, *apiError) {
|
||||||
ts = api.now()
|
ts = api.now()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx := api.context(r)
|
||||||
|
if to := r.FormValue("timeout"); to != "" {
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
timeout, err := parseDuration(to)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &apiError{errorBadData, err}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel = context.WithTimeout(ctx, timeout)
|
||||||
|
defer cancel()
|
||||||
|
}
|
||||||
|
|
||||||
qry, err := api.QueryEngine.NewInstantQuery(r.FormValue("query"), ts)
|
qry, err := api.QueryEngine.NewInstantQuery(r.FormValue("query"), ts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &apiError{errorBadData, err}
|
return nil, &apiError{errorBadData, err}
|
||||||
}
|
}
|
||||||
|
|
||||||
res := qry.Exec(api.context(r))
|
res := qry.Exec(ctx)
|
||||||
if res.Err != nil {
|
if res.Err != nil {
|
||||||
switch res.Err.(type) {
|
switch res.Err.(type) {
|
||||||
case promql.ErrQueryCanceled:
|
case promql.ErrQueryCanceled:
|
||||||
|
@ -222,12 +234,24 @@ func (api *API) queryRange(r *http.Request) (interface{}, *apiError) {
|
||||||
return nil, &apiError{errorBadData, err}
|
return nil, &apiError{errorBadData, err}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx := api.context(r)
|
||||||
|
if to := r.FormValue("timeout"); to != "" {
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
timeout, err := parseDuration(to)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &apiError{errorBadData, err}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel = context.WithTimeout(ctx, timeout)
|
||||||
|
defer cancel()
|
||||||
|
}
|
||||||
|
|
||||||
qry, err := api.QueryEngine.NewRangeQuery(r.FormValue("query"), start, end, step)
|
qry, err := api.QueryEngine.NewRangeQuery(r.FormValue("query"), start, end, step)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &apiError{errorBadData, err}
|
return nil, &apiError{errorBadData, err}
|
||||||
}
|
}
|
||||||
|
|
||||||
res := qry.Exec(api.context(r))
|
res := qry.Exec(ctx)
|
||||||
if res.Err != nil {
|
if res.Err != nil {
|
||||||
switch res.Err.(type) {
|
switch res.Err.(type) {
|
||||||
case promql.ErrQueryCanceled:
|
case promql.ErrQueryCanceled:
|
||||||
|
@ -361,6 +385,7 @@ func (api *API) dropSeries(r *http.Request) (interface{}, *apiError) {
|
||||||
// return res, nil
|
// return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Target has the information for one target.
|
||||||
type Target struct {
|
type Target struct {
|
||||||
// Labels before any processing.
|
// Labels before any processing.
|
||||||
DiscoveredLabels map[string]string `json:"discoveredLabels"`
|
DiscoveredLabels map[string]string `json:"discoveredLabels"`
|
||||||
|
@ -374,6 +399,7 @@ type Target struct {
|
||||||
Health retrieval.TargetHealth `json:"health"`
|
Health retrieval.TargetHealth `json:"health"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TargetDiscovery has all the active targets.
|
||||||
type TargetDiscovery struct {
|
type TargetDiscovery struct {
|
||||||
ActiveTargets []*Target `json:"activeTargets"`
|
ActiveTargets []*Target `json:"activeTargets"`
|
||||||
}
|
}
|
||||||
|
@ -402,10 +428,12 @@ func (api *API) targets(r *http.Request) (interface{}, *apiError) {
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AlertmanagerDiscovery has all the active Alertmanagers.
|
||||||
type AlertmanagerDiscovery struct {
|
type AlertmanagerDiscovery struct {
|
||||||
ActiveAlertmanagers []*AlertmanagerTarget `json:"activeAlertmanagers"`
|
ActiveAlertmanagers []*AlertmanagerTarget `json:"activeAlertmanagers"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AlertmanagerTarget has info on one AM.
|
||||||
type AlertmanagerTarget struct {
|
type AlertmanagerTarget struct {
|
||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
}
|
}
|
||||||
|
@ -476,7 +504,11 @@ func parseTime(s string) (time.Time, error) {
|
||||||
|
|
||||||
func parseDuration(s string) (time.Duration, error) {
|
func parseDuration(s string) (time.Duration, error) {
|
||||||
if d, err := strconv.ParseFloat(s, 64); err == nil {
|
if d, err := strconv.ParseFloat(s, 64); err == nil {
|
||||||
return time.Duration(d * float64(time.Second)), nil
|
ts := d * float64(time.Second)
|
||||||
|
if ts > float64(math.MaxInt64) || ts < float64(math.MinInt64) {
|
||||||
|
return 0, fmt.Errorf("cannot parse %q to a valid duration. It overflows int64", s)
|
||||||
|
}
|
||||||
|
return time.Duration(ts), nil
|
||||||
}
|
}
|
||||||
if d, err := model.ParseDuration(s); err == nil {
|
if d, err := model.ParseDuration(s); err == nil {
|
||||||
return time.Duration(d), nil
|
return time.Duration(d), nil
|
||||||
|
|
|
@ -224,7 +224,7 @@ func TestEndpoints(t *testing.T) {
|
||||||
},
|
},
|
||||||
errType: errorBadData,
|
errType: errorBadData,
|
||||||
},
|
},
|
||||||
// Invalid step
|
// Invalid step.
|
||||||
{
|
{
|
||||||
endpoint: api.queryRange,
|
endpoint: api.queryRange,
|
||||||
query: url.Values{
|
query: url.Values{
|
||||||
|
@ -235,7 +235,7 @@ func TestEndpoints(t *testing.T) {
|
||||||
},
|
},
|
||||||
errType: errorBadData,
|
errType: errorBadData,
|
||||||
},
|
},
|
||||||
// Start after end
|
// Start after end.
|
||||||
{
|
{
|
||||||
endpoint: api.queryRange,
|
endpoint: api.queryRange,
|
||||||
query: url.Values{
|
query: url.Values{
|
||||||
|
@ -246,6 +246,17 @@ func TestEndpoints(t *testing.T) {
|
||||||
},
|
},
|
||||||
errType: errorBadData,
|
errType: errorBadData,
|
||||||
},
|
},
|
||||||
|
// Start overflows int64 internally.
|
||||||
|
{
|
||||||
|
endpoint: api.queryRange,
|
||||||
|
query: url.Values{
|
||||||
|
"query": []string{"time()"},
|
||||||
|
"start": []string{"148966367200.372"},
|
||||||
|
"end": []string{"1489667272.372"},
|
||||||
|
"step": []string{"1"},
|
||||||
|
},
|
||||||
|
errType: errorBadData,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
endpoint: api.labelValues,
|
endpoint: api.labelValues,
|
||||||
params: map[string]string{
|
params: map[string]string{
|
||||||
|
@ -567,9 +578,6 @@ func TestParseTime(t *testing.T) {
|
||||||
}, {
|
}, {
|
||||||
input: "123.123",
|
input: "123.123",
|
||||||
result: time.Unix(123, 123000000),
|
result: time.Unix(123, 123000000),
|
||||||
}, {
|
|
||||||
input: "123.123",
|
|
||||||
result: time.Unix(123, 123000000),
|
|
||||||
}, {
|
}, {
|
||||||
input: "2015-06-03T13:21:58.555Z",
|
input: "2015-06-03T13:21:58.555Z",
|
||||||
result: ts,
|
result: ts,
|
||||||
|
@ -610,6 +618,14 @@ func TestParseDuration(t *testing.T) {
|
||||||
}, {
|
}, {
|
||||||
input: "2015-06-03T13:21:58.555Z",
|
input: "2015-06-03T13:21:58.555Z",
|
||||||
fail: true,
|
fail: true,
|
||||||
|
}, {
|
||||||
|
// Internal int64 overflow.
|
||||||
|
input: "-148966367200.372",
|
||||||
|
fail: true,
|
||||||
|
}, {
|
||||||
|
// Internal int64 overflow.
|
||||||
|
input: "148966367200.372",
|
||||||
|
fail: true,
|
||||||
}, {
|
}, {
|
||||||
input: "123",
|
input: "123",
|
||||||
result: 123 * time.Second,
|
result: 123 * time.Second,
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -46,7 +46,10 @@ Prometheus.Graph.prototype.initialize = function() {
|
||||||
|
|
||||||
// Draw graph controls and container from Handlebars template.
|
// Draw graph controls and container from Handlebars template.
|
||||||
|
|
||||||
var options = {'pathPrefix': PATH_PREFIX};
|
var options = {
|
||||||
|
'pathPrefix': PATH_PREFIX,
|
||||||
|
'buildVersion': BUILD_VERSION
|
||||||
|
};
|
||||||
jQuery.extend(options, self.options);
|
jQuery.extend(options, self.options);
|
||||||
self.graphHTML = $(Mustache.render(graphTemplate, options));
|
self.graphHTML = $(Mustache.render(graphTemplate, options));
|
||||||
self.el.append(self.graphHTML);
|
self.el.append(self.graphHTML);
|
||||||
|
@ -212,13 +215,12 @@ Prometheus.Graph.prototype.populateInsertableMetrics = function() {
|
||||||
items: "all",
|
items: "all",
|
||||||
matcher: function(item) {
|
matcher: function(item) {
|
||||||
// If we have result for current query, skip
|
// If we have result for current query, skip
|
||||||
if (!self.fuzzyResult.query || self.fuzzyResult.query !== this.query) {
|
if (self.fuzzyResult.query !== this.query) {
|
||||||
self.fuzzyResult.query = this.query;
|
self.fuzzyResult.query = this.query;
|
||||||
self.fuzzyResult.map = {};
|
self.fuzzyResult.map = {};
|
||||||
self.fuzzyResult.result = fuzzy.filter(this.query.replace('_', ' '), metrics, {
|
self.fuzzyResult.result = fuzzy.filter(this.query.replace(/ /g, ''), metrics, {
|
||||||
pre: '<strong>',
|
pre: '<strong>',
|
||||||
post: '</strong>',
|
post: '</strong>'
|
||||||
extract: function(el) { return el.replace('_', ' ') }
|
|
||||||
});
|
});
|
||||||
self.fuzzyResult.result.forEach(function(r) {
|
self.fuzzyResult.result.forEach(function(r) {
|
||||||
self.fuzzyResult.map[r.original] = r;
|
self.fuzzyResult.map[r.original] = r;
|
||||||
|
@ -237,7 +239,7 @@ Prometheus.Graph.prototype.populateInsertableMetrics = function() {
|
||||||
},
|
},
|
||||||
|
|
||||||
highlighter: function (item) {
|
highlighter: function (item) {
|
||||||
return $('<div>' + self.fuzzyResult.map[item].string.replace(' ', '_') + '</div>')
|
return $('<div>' + self.fuzzyResult.map[item].string + '</div>')
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
// This needs to happen after attaching the typeahead plugin, as it
|
// This needs to happen after attaching the typeahead plugin, as it
|
||||||
|
@ -820,7 +822,7 @@ function init() {
|
||||||
});
|
});
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: PATH_PREFIX + "/static/js/graph_template.handlebar",
|
url: PATH_PREFIX + "/static/js/graph_template.handlebar?v=" + BUILD_VERSION,
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
|
|
||||||
graphTemplate = data;
|
graphTemplate = data;
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-2">
|
<div class="col-lg-2">
|
||||||
<div class="eval_stats pull-right"></div>
|
<div class="eval_stats pull-right"></div>
|
||||||
<img src="{{ pathPrefix }}/static/img/ajax-loader.gif" class="spinner" alt="ajax_spinner">
|
<img src="{{ pathPrefix }}/static/img/ajax-loader.gif?v={{ buildVersion }}" class="spinner" alt="ajax_spinner">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
|
@ -523,6 +523,14 @@ PromConsole.Graph.prototype._clearGraph = function() {
|
||||||
|
|
||||||
PromConsole.Graph.prototype._xhrs = [];
|
PromConsole.Graph.prototype._xhrs = [];
|
||||||
|
|
||||||
|
PromConsole.Graph.prototype.buildQueryUrl = function(expr) {
|
||||||
|
var p = this.params;
|
||||||
|
return PATH_PREFIX + "/api/v1/query_range?query=" +
|
||||||
|
encodeURIComponent(expr) +
|
||||||
|
"&step=" + p.duration / this.graphTd.offsetWidth +
|
||||||
|
"&start=" + (p.endTime - p.duration) + "&end=" + p.endTime;
|
||||||
|
};
|
||||||
|
|
||||||
PromConsole.Graph.prototype.dispatch = function() {
|
PromConsole.Graph.prototype.dispatch = function() {
|
||||||
for (var j = 0; j < this._xhrs.length; j++) {
|
for (var j = 0; j < this._xhrs.length; j++) {
|
||||||
this._xhrs[j].abort();
|
this._xhrs[j].abort();
|
||||||
|
@ -532,9 +540,7 @@ PromConsole.Graph.prototype.dispatch = function() {
|
||||||
var pending_requests = this.params.expr.length;
|
var pending_requests = this.params.expr.length;
|
||||||
for (var i = 0; i < this.params.expr.length; ++i) {
|
for (var i = 0; i < this.params.expr.length; ++i) {
|
||||||
var endTime = this.params.endTime;
|
var endTime = this.params.endTime;
|
||||||
var url = PATH_PREFIX + "/api/v1/query_range?query=" + encodeURIComponent(this.params.expr[i]) +
|
var url = this.buildQueryUrl(this.params.expr[i]);
|
||||||
"&step=" + this.params.duration / this.graphTd.offsetWidth +
|
|
||||||
"&start=" + (endTime - this.params.duration) + "&end=" + endTime;
|
|
||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
xhr.open('get', url, true);
|
xhr.open('get', url, true);
|
||||||
xhr.responseType = 'json';
|
xhr.responseType = 'json';
|
||||||
|
@ -572,7 +578,7 @@ PromConsole.Graph.prototype.dispatch = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
var loadingImg = document.createElement("img");
|
var loadingImg = document.createElement("img");
|
||||||
loadingImg.src = PATH_PREFIX + '/static/img/ajax-loader.gif';
|
loadingImg.src = PATH_PREFIX + '/static/img/ajax-loader.gif?v=' + BUILD_VERSION;
|
||||||
loadingImg.alt = 'Loading...';
|
loadingImg.alt = 'Loading...';
|
||||||
loadingImg.className = 'prom_graph_loading';
|
loadingImg.className = 'prom_graph_loading';
|
||||||
this.graphTd.appendChild(loadingImg);
|
this.graphTd.appendChild(loadingImg);
|
||||||
|
|
26
web/ui/static/vendor/fuzzy.js
vendored
26
web/ui/static/vendor/fuzzy.js
vendored
|
@ -1,26 +0,0 @@
|
||||||
// Copyright (c) 2012 Matt York
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person
|
|
||||||
// obtaining a copy of this software and associated documentation
|
|
||||||
// files (the "Software"), to deal in the Software without
|
|
||||||
// restriction, including without limitation the rights to use,
|
|
||||||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the
|
|
||||||
// Software is furnished to do so, subject to the following
|
|
||||||
// conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be
|
|
||||||
// included in all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
||||||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
||||||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
||||||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
||||||
// OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
//
|
|
||||||
// https://github.com/mattyork/fuzzy/blob/3613086aa40c180ca722aeaf48cef575dc57eb5d/fuzzy-min.js
|
|
||||||
|
|
||||||
(function(){var root=this;var fuzzy={};if(typeof exports!=="undefined"){module.exports=fuzzy}else{root.fuzzy=fuzzy}fuzzy.simpleFilter=function(pattern,array){return array.filter(function(str){return fuzzy.test(pattern,str)})};fuzzy.test=function(pattern,str){return fuzzy.match(pattern,str)!==null};fuzzy.match=function(pattern,str,opts){opts=opts||{};var patternIdx=0,result=[],len=str.length,totalScore=0,currScore=0,pre=opts.pre||"",post=opts.post||"",compareString=opts.caseSensitive&&str||str.toLowerCase(),ch;pattern=opts.caseSensitive&&pattern||pattern.toLowerCase();for(var idx=0;idx<len;idx++){ch=str[idx];if(compareString[idx]===pattern[patternIdx]){ch=pre+ch+post;patternIdx+=1;currScore+=1+currScore}else{currScore=0}totalScore+=currScore;result[result.length]=ch}if(patternIdx===pattern.length){totalScore=compareString===pattern?Infinity:totalScore;return{rendered:result.join(""),score:totalScore}}return null};fuzzy.filter=function(pattern,arr,opts){if(!arr||arr.length===0){return[]}if(typeof pattern!=="string"){return arr}opts=opts||{};return arr.reduce(function(prev,element,idx,arr){var str=element;if(opts.extract){str=opts.extract(element)}var rendered=fuzzy.match(pattern,str,opts);if(rendered!=null){prev[prev.length]={string:rendered.rendered,score:rendered.score,index:idx,original:element}}return prev},[]).sort(function(a,b){var compare=b.score-a.score;if(compare)return compare;return a.index-b.index})}})();
|
|
182
web/ui/static/vendor/fuzzy/fuzzy.js
vendored
Normal file
182
web/ui/static/vendor/fuzzy/fuzzy.js
vendored
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
/*
|
||||||
|
* Fuzzy
|
||||||
|
* https://github.com/myork/fuzzy
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012 Matt York
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person
|
||||||
|
* obtaining a copy of this software and associated documentation
|
||||||
|
* files (the "Software"), to deal in the Software without
|
||||||
|
* restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following
|
||||||
|
* conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* A slightly modified version of https://github.com/mattyork/fuzzy/blob/3613086aa40c180ca722aeaf48cef575dc57eb5d/lib/fuzzy.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
var root = this;
|
||||||
|
|
||||||
|
var fuzzy = {};
|
||||||
|
|
||||||
|
// Use in node or in browser
|
||||||
|
if (typeof exports !== 'undefined') {
|
||||||
|
module.exports = fuzzy;
|
||||||
|
} else {
|
||||||
|
root.fuzzy = fuzzy;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return all elements of `array` that have a fuzzy
|
||||||
|
// match against `pattern`.
|
||||||
|
fuzzy.simpleFilter = function(pattern, array) {
|
||||||
|
return array.filter(function(str) {
|
||||||
|
return fuzzy.test(pattern, str);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Does `pattern` fuzzy match `str`?
|
||||||
|
fuzzy.test = function(pattern, str) {
|
||||||
|
return fuzzy.match(pattern, str) !== null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// If `pattern` matches `str`, wrap each matching character
|
||||||
|
// in `opts.pre` and `opts.post`. If no match, return null
|
||||||
|
fuzzy.match = function(pattern, str, opts, _fromIndex) {
|
||||||
|
opts = opts || {};
|
||||||
|
var patternIdx = 0
|
||||||
|
, result = []
|
||||||
|
, len = str.length
|
||||||
|
, fromIndex = _fromIndex || 0
|
||||||
|
, totalScore = 0
|
||||||
|
, currScore = 0
|
||||||
|
// prefix
|
||||||
|
, pre = opts.pre || ''
|
||||||
|
// suffix
|
||||||
|
, post = opts.post || ''
|
||||||
|
// String to compare against. This might be a lowercase version of the
|
||||||
|
// raw string
|
||||||
|
, compareString = opts.caseSensitive && str || str.toLowerCase()
|
||||||
|
, ch;
|
||||||
|
|
||||||
|
pattern = opts.caseSensitive && pattern || pattern.toLowerCase();
|
||||||
|
|
||||||
|
// If there's an exact match, add pre/post, max out score and skip the lookup
|
||||||
|
if (compareString === pattern) {
|
||||||
|
return {
|
||||||
|
rendered: pre + compareString.split('').join(post+pre) + post,
|
||||||
|
score: Infinity
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each character in the string, either add it to the result
|
||||||
|
// or wrap in template if it's the next string in the pattern
|
||||||
|
for(var idx = 0; idx < len; idx++) {
|
||||||
|
ch = str[idx];
|
||||||
|
if(idx >= fromIndex && compareString[idx] === pattern[patternIdx]) {
|
||||||
|
ch = pre + ch + post;
|
||||||
|
patternIdx += 1;
|
||||||
|
|
||||||
|
// consecutive characters should increase the score more than linearly
|
||||||
|
currScore += 1 + currScore;
|
||||||
|
} else {
|
||||||
|
currScore = 0;
|
||||||
|
}
|
||||||
|
totalScore += currScore;
|
||||||
|
result[result.length] = ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return rendered string if we have a match for every char
|
||||||
|
if(patternIdx === pattern.length) {
|
||||||
|
var nextPossible = str.indexOf(pattern[0], str.indexOf(pattern[0], fromIndex) + 1)
|
||||||
|
, candidate;
|
||||||
|
|
||||||
|
// If possible, try to find a better match at the rest of the string
|
||||||
|
if (nextPossible > -1 && str.length - nextPossible >= pattern.length) {
|
||||||
|
var candidate = fuzzy.match(pattern, str, opts, nextPossible);
|
||||||
|
}
|
||||||
|
|
||||||
|
return candidate && candidate.score > totalScore ? candidate : {
|
||||||
|
rendered: result.join(''), score: totalScore
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The normal entry point. Filters `arr` for matches against `pattern`.
|
||||||
|
// It returns an array with matching values of the type:
|
||||||
|
//
|
||||||
|
// [{
|
||||||
|
// string: '<b>lah' // The rendered string
|
||||||
|
// , index: 2 // The index of the element in `arr`
|
||||||
|
// , original: 'blah' // The original element in `arr`
|
||||||
|
// }]
|
||||||
|
//
|
||||||
|
// `opts` is an optional argument bag. Details:
|
||||||
|
//
|
||||||
|
// opts = {
|
||||||
|
// // string to put before a matching character
|
||||||
|
// pre: '<b>'
|
||||||
|
//
|
||||||
|
// // string to put after matching character
|
||||||
|
// , post: '</b>'
|
||||||
|
//
|
||||||
|
// // Optional function. Input is an entry in the given arr`,
|
||||||
|
// // output should be the string to test `pattern` against.
|
||||||
|
// // In this example, if `arr = [{crying: 'koala'}]` we would return
|
||||||
|
// // 'koala'.
|
||||||
|
// , extract: function(arg) { return arg.crying; }
|
||||||
|
// }
|
||||||
|
fuzzy.filter = function(pattern, arr, opts) {
|
||||||
|
if(!arr || arr.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
if (typeof pattern !== 'string' || pattern === '') {
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
opts = opts || {};
|
||||||
|
return arr
|
||||||
|
.reduce(function(prev, element, idx, arr) {
|
||||||
|
var str = element;
|
||||||
|
if(opts.extract) {
|
||||||
|
str = opts.extract(element);
|
||||||
|
}
|
||||||
|
var rendered = fuzzy.match(pattern, str, opts);
|
||||||
|
if(rendered != null) {
|
||||||
|
prev[prev.length] = {
|
||||||
|
string: rendered.rendered
|
||||||
|
, score: rendered.score
|
||||||
|
, index: idx
|
||||||
|
, original: element
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return prev;
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// Sort by score. Browsers are inconsistent wrt stable/unstable
|
||||||
|
// sorting, so force stable by using the index in the case of tie.
|
||||||
|
// See http://ofb.net/~sethml/is-sort-stable.html
|
||||||
|
.sort(function(a,b) {
|
||||||
|
var compare = b.score - a.score;
|
||||||
|
if(compare) return compare;
|
||||||
|
return a.index - b.index;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}());
|
|
@ -3,15 +3,16 @@
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
<title>Prometheus Time Series Collection and Processing Server</title>
|
<title>Prometheus Time Series Collection and Processing Server</title>
|
||||||
<link rel="shortcut icon" href="{{ pathPrefix }}/static/img/favicon.ico">
|
<link rel="shortcut icon" href="{{ pathPrefix }}/static/img/favicon.ico?v={{ buildVersion }}">
|
||||||
<script src="{{ pathPrefix }}/static/vendor/js/jquery.min.js"></script>
|
<script src="{{ pathPrefix }}/static/vendor/js/jquery.min.js?v={{ buildVersion }}"></script>
|
||||||
<script src="{{ pathPrefix }}/static/vendor/bootstrap-3.3.1/js/bootstrap.min.js"></script>
|
<script src="{{ pathPrefix }}/static/vendor/bootstrap-3.3.1/js/bootstrap.min.js?v={{ buildVersion }}"></script>
|
||||||
|
|
||||||
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/static/vendor/bootstrap-3.3.1/css/bootstrap.min.css">
|
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/static/vendor/bootstrap-3.3.1/css/bootstrap.min.css?v={{ buildVersion }}">
|
||||||
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/static/css/prometheus.css">
|
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/static/css/prometheus.css?v={{ buildVersion }}">
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var PATH_PREFIX = "{{ pathPrefix }}";
|
var PATH_PREFIX = "{{ pathPrefix }}";
|
||||||
|
var BUILD_VERSION = "{{ buildVersion }}";
|
||||||
$(function () {
|
$(function () {
|
||||||
$('[data-toggle="tooltip"]').tooltip()
|
$('[data-toggle="tooltip"]').tooltip()
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{define "head"}}
|
{{define "head"}}
|
||||||
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/static/css/alerts.css">
|
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/static/css/alerts.css?v={{ buildVersion }}">
|
||||||
<script src="{{ pathPrefix }}/static/js/alerts.js"></script>
|
<script src="{{ pathPrefix }}/static/js/alerts.js?v={{ buildVersion }}"></script>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{define "content"}}
|
{{define "content"}}
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
{{define "head"}}
|
{{define "head"}}
|
||||||
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/static/css/graph.css">
|
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/static/css/graph.css?v={{ buildVersion }}">
|
||||||
|
|
||||||
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/static/vendor/rickshaw/rickshaw.min.css">
|
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/static/vendor/rickshaw/rickshaw.min.css?v={{ buildVersion }}">
|
||||||
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/static/vendor/eonasdan-bootstrap-datetimepicker/bootstrap-datetimepicker.min.css">
|
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/static/vendor/eonasdan-bootstrap-datetimepicker/bootstrap-datetimepicker.min.css?v={{ buildVersion }}">
|
||||||
|
|
||||||
<script src="{{ pathPrefix }}/static/vendor/rickshaw/vendor/d3.v3.js"></script>
|
<script src="{{ pathPrefix }}/static/vendor/rickshaw/vendor/d3.v3.js?v={{ buildVersion }}"></script>
|
||||||
<script src="{{ pathPrefix }}/static/vendor/rickshaw/vendor/d3.layout.min.js"></script>
|
<script src="{{ pathPrefix }}/static/vendor/rickshaw/vendor/d3.layout.min.js?v={{ buildVersion }}"></script>
|
||||||
<script src="{{ pathPrefix }}/static/vendor/rickshaw/rickshaw.min.js"></script>
|
<script src="{{ pathPrefix }}/static/vendor/rickshaw/rickshaw.min.js?v={{ buildVersion }}"></script>
|
||||||
<script src="{{ pathPrefix }}/static/vendor/moment/moment.min.js"></script>
|
<script src="{{ pathPrefix }}/static/vendor/moment/moment.min.js?v={{ buildVersion }}"></script>
|
||||||
<script src="{{ pathPrefix }}/static/vendor/eonasdan-bootstrap-datetimepicker/bootstrap-datetimepicker.min.js"></script>
|
<script src="{{ pathPrefix }}/static/vendor/eonasdan-bootstrap-datetimepicker/bootstrap-datetimepicker.min.js?v={{ buildVersion }}"></script>
|
||||||
<script src="{{ pathPrefix }}/static/vendor/bootstrap3-typeahead/bootstrap3-typeahead.min.js"></script>
|
<script src="{{ pathPrefix }}/static/vendor/bootstrap3-typeahead/bootstrap3-typeahead.min.js?v={{ buildVersion }}"></script>
|
||||||
<script src="{{ pathPrefix }}/static/vendor/fuzzy.js"></script>
|
<script src="{{ pathPrefix }}/static/vendor/fuzzy/fuzzy.js?v={{ buildVersion }}"></script>
|
||||||
|
|
||||||
<script src="{{ pathPrefix }}/static/vendor/mustache/mustache.min.js"></script>
|
<script src="{{ pathPrefix }}/static/vendor/mustache/mustache.min.js?v={{ buildVersion }}"></script>
|
||||||
<script src="{{ pathPrefix }}/static/vendor/js/jquery.selection.js"></script>
|
<script src="{{ pathPrefix }}/static/vendor/js/jquery.selection.js?v={{ buildVersion }}"></script>
|
||||||
<script src="{{ pathPrefix }}/static/vendor/js/jquery.hotkeys.js"></script>
|
<script src="{{ pathPrefix }}/static/vendor/js/jquery.hotkeys.js?v={{ buildVersion }}"></script>
|
||||||
|
|
||||||
<script src="{{ pathPrefix }}/static/js/graph.js?v=1"></script>
|
<script src="{{ pathPrefix }}/static/js/graph.js?v={{ buildVersion }}"></script>
|
||||||
|
|
||||||
<script id="graph_template" type="text/x-handlebars-template"></script>
|
<script id="graph_template" type="text/x-handlebars-template"></script>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -425,7 +425,8 @@ func tmplFuncs(consolesPath string, opts *Options) template_text.FuncMap {
|
||||||
},
|
},
|
||||||
"consolesPath": func() string { return consolesPath },
|
"consolesPath": func() string { return consolesPath },
|
||||||
"pathPrefix": func() string { return opts.ExternalURL.Path },
|
"pathPrefix": func() string { return opts.ExternalURL.Path },
|
||||||
"stripLabels": func(lset map[string]string, labels ...string) map[string]string {
|
"buildVersion": func() string { return opts.Version.Revision[:7] },
|
||||||
|
"stripLabels": func(lset model.LabelSet, labels ...model.LabelName) model.LabelSet {
|
||||||
for _, ln := range labels {
|
for _, ln := range labels {
|
||||||
delete(lset, ln)
|
delete(lset, ln)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue