prometheus
237 строк · 8.0 Кб
1// Copyright 2020 The Prometheus Authors
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14package eureka15
16import (17"context"18"errors"19"fmt"20"net"21"net/http"22"net/url"23"strconv"24"time"25
26"github.com/go-kit/log"27"github.com/prometheus/client_golang/prometheus"28"github.com/prometheus/common/config"29"github.com/prometheus/common/model"30
31"github.com/prometheus/prometheus/discovery"32"github.com/prometheus/prometheus/discovery/refresh"33"github.com/prometheus/prometheus/discovery/targetgroup"34"github.com/prometheus/prometheus/util/strutil"35)
36
37const (38// metaLabelPrefix is the meta prefix used for all meta labels.39// in this discovery.40metaLabelPrefix = model.MetaLabelPrefix + "eureka_"41metaAppInstanceLabelPrefix = metaLabelPrefix + "app_instance_"42
43appNameLabel = metaLabelPrefix + "app_name"44appInstanceHostNameLabel = metaAppInstanceLabelPrefix + "hostname"45appInstanceHomePageURLLabel = metaAppInstanceLabelPrefix + "homepage_url"46appInstanceStatusPageURLLabel = metaAppInstanceLabelPrefix + "statuspage_url"47appInstanceHealthCheckURLLabel = metaAppInstanceLabelPrefix + "healthcheck_url"48appInstanceIPAddrLabel = metaAppInstanceLabelPrefix + "ip_addr"49appInstanceVipAddressLabel = metaAppInstanceLabelPrefix + "vip_address"50appInstanceSecureVipAddressLabel = metaAppInstanceLabelPrefix + "secure_vip_address"51appInstanceStatusLabel = metaAppInstanceLabelPrefix + "status"52appInstancePortLabel = metaAppInstanceLabelPrefix + "port"53appInstancePortEnabledLabel = metaAppInstanceLabelPrefix + "port_enabled"54appInstanceSecurePortLabel = metaAppInstanceLabelPrefix + "secure_port"55appInstanceSecurePortEnabledLabel = metaAppInstanceLabelPrefix + "secure_port_enabled"56appInstanceDataCenterInfoNameLabel = metaAppInstanceLabelPrefix + "datacenterinfo_name"57appInstanceDataCenterInfoMetadataPrefix = metaAppInstanceLabelPrefix + "datacenterinfo_metadata_"58appInstanceCountryIDLabel = metaAppInstanceLabelPrefix + "country_id"59appInstanceIDLabel = metaAppInstanceLabelPrefix + "id"60appInstanceMetadataPrefix = metaAppInstanceLabelPrefix + "metadata_"61)
62
63// DefaultSDConfig is the default Eureka SD configuration.
64var DefaultSDConfig = SDConfig{65RefreshInterval: model.Duration(30 * time.Second),66HTTPClientConfig: config.DefaultHTTPClientConfig,67}
68
69func init() {70discovery.RegisterConfig(&SDConfig{})71}
72
73// SDConfig is the configuration for applications running on Eureka.
74type SDConfig struct {75Server string `yaml:"server,omitempty"`76HTTPClientConfig config.HTTPClientConfig `yaml:",inline"`77RefreshInterval model.Duration `yaml:"refresh_interval,omitempty"`78}
79
80// NewDiscovererMetrics implements discovery.Config.
81func (*SDConfig) NewDiscovererMetrics(reg prometheus.Registerer, rmi discovery.RefreshMetricsInstantiator) discovery.DiscovererMetrics {82return &eurekaMetrics{83refreshMetrics: rmi,84}85}
86
87// Name returns the name of the Config.
88func (*SDConfig) Name() string { return "eureka" }89
90// NewDiscoverer returns a Discoverer for the Config.
91func (c *SDConfig) NewDiscoverer(opts discovery.DiscovererOptions) (discovery.Discoverer, error) {92return NewDiscovery(c, opts.Logger, opts.Metrics)93}
94
95// SetDirectory joins any relative file paths with dir.
96func (c *SDConfig) SetDirectory(dir string) {97c.HTTPClientConfig.SetDirectory(dir)98}
99
100// UnmarshalYAML implements the yaml.Unmarshaler interface.
101func (c *SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {102*c = DefaultSDConfig103type plain SDConfig104err := unmarshal((*plain)(c))105if err != nil {106return err107}108if len(c.Server) == 0 {109return errors.New("eureka_sd: empty or null eureka server")110}111url, err := url.Parse(c.Server)112if err != nil {113return err114}115if len(url.Scheme) == 0 || len(url.Host) == 0 {116return errors.New("eureka_sd: invalid eureka server URL")117}118return c.HTTPClientConfig.Validate()119}
120
121// Discovery provides service discovery based on a Eureka instance.
122type Discovery struct {123*refresh.Discovery124client *http.Client125server string126}
127
128// NewDiscovery creates a new Eureka discovery for the given role.
129func NewDiscovery(conf *SDConfig, logger log.Logger, metrics discovery.DiscovererMetrics) (*Discovery, error) {130m, ok := metrics.(*eurekaMetrics)131if !ok {132return nil, fmt.Errorf("invalid discovery metrics type")133}134
135rt, err := config.NewRoundTripperFromConfig(conf.HTTPClientConfig, "eureka_sd")136if err != nil {137return nil, err138}139
140d := &Discovery{141client: &http.Client{Transport: rt},142server: conf.Server,143}144d.Discovery = refresh.NewDiscovery(145refresh.Options{146Logger: logger,147Mech: "eureka",148Interval: time.Duration(conf.RefreshInterval),149RefreshF: d.refresh,150MetricsInstantiator: m.refreshMetrics,151},152)153return d, nil154}
155
156func (d *Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) {157apps, err := fetchApps(ctx, d.server, d.client)158if err != nil {159return nil, err160}161
162tg := &targetgroup.Group{163Source: "eureka",164}165
166for _, app := range apps.Applications {167targets := targetsForApp(&app)168tg.Targets = append(tg.Targets, targets...)169}170return []*targetgroup.Group{tg}, nil171}
172
173func targetsForApp(app *Application) []model.LabelSet {174targets := make([]model.LabelSet, 0, len(app.Instances))175
176// Gather info about the app's 'instances'. Each instance is considered a task.177for _, t := range app.Instances {178var targetAddress string179if t.Port != nil {180targetAddress = net.JoinHostPort(t.HostName, strconv.Itoa(t.Port.Port))181} else {182targetAddress = net.JoinHostPort(t.HostName, "80")183}184
185target := model.LabelSet{186model.AddressLabel: lv(targetAddress),187model.InstanceLabel: lv(t.InstanceID),188
189appNameLabel: lv(app.Name),190appInstanceHostNameLabel: lv(t.HostName),191appInstanceHomePageURLLabel: lv(t.HomePageURL),192appInstanceStatusPageURLLabel: lv(t.StatusPageURL),193appInstanceHealthCheckURLLabel: lv(t.HealthCheckURL),194appInstanceIPAddrLabel: lv(t.IPAddr),195appInstanceVipAddressLabel: lv(t.VipAddress),196appInstanceSecureVipAddressLabel: lv(t.SecureVipAddress),197appInstanceStatusLabel: lv(t.Status),198appInstanceCountryIDLabel: lv(strconv.Itoa(t.CountryID)),199appInstanceIDLabel: lv(t.InstanceID),200}201
202if t.Port != nil {203target[appInstancePortLabel] = lv(strconv.Itoa(t.Port.Port))204target[appInstancePortEnabledLabel] = lv(strconv.FormatBool(t.Port.Enabled))205}206
207if t.SecurePort != nil {208target[appInstanceSecurePortLabel] = lv(strconv.Itoa(t.SecurePort.Port))209target[appInstanceSecurePortEnabledLabel] = lv(strconv.FormatBool(t.SecurePort.Enabled))210}211
212if t.DataCenterInfo != nil {213target[appInstanceDataCenterInfoNameLabel] = lv(t.DataCenterInfo.Name)214
215if t.DataCenterInfo.Metadata != nil {216for _, m := range t.DataCenterInfo.Metadata.Items {217ln := strutil.SanitizeLabelName(m.XMLName.Local)218target[model.LabelName(appInstanceDataCenterInfoMetadataPrefix+ln)] = lv(m.Content)219}220}221}222
223if t.Metadata != nil {224for _, m := range t.Metadata.Items {225ln := strutil.SanitizeLabelName(m.XMLName.Local)226target[model.LabelName(appInstanceMetadataPrefix+ln)] = lv(m.Content)227}228}229
230targets = append(targets, target)231}232return targets233}
234
235func lv(s string) model.LabelValue {236return model.LabelValue(s)237}
238