From 70f3d1e9f95ad6611be2e76fad44f07b0a2579ca Mon Sep 17 00:00:00 2001 From: Johannes 'fish' Ziemke Date: Mon, 4 Sep 2017 13:10:44 +0200 Subject: [PATCH 1/6] k8s: Support discovery of ingresses (#3111) * k8s: Support discovery of ingresses * Move additional labels below allocation This makes it more obvious why the additional elements are allocated. Also fix allocation for node where we only set a single label. * k8s: Remove port from ingress discovery * k8s: Add comment to ingress discovery example --- config/config.go | 11 +- discovery/kubernetes/endpoints.go | 21 +- discovery/kubernetes/ingress.go | 184 ++++++++++++++++++ discovery/kubernetes/ingress_test.go | 137 +++++++++++++ discovery/kubernetes/kubernetes.go | 22 +++ discovery/kubernetes/node.go | 23 +-- discovery/kubernetes/pod.go | 21 +- discovery/kubernetes/service.go | 24 +-- .../examples/prometheus-kubernetes.yml | 36 +++- 9 files changed, 430 insertions(+), 49 deletions(-) create mode 100644 discovery/kubernetes/ingress.go create mode 100644 discovery/kubernetes/ingress_test.go diff --git a/config/config.go b/config/config.go index 47baf2e0d1..21134e1b99 100644 --- a/config/config.go +++ b/config/config.go @@ -1005,10 +1005,11 @@ type KubernetesRole string // The valid options for KubernetesRole. const ( - KubernetesRoleNode = "node" - KubernetesRolePod = "pod" - KubernetesRoleService = "service" - KubernetesRoleEndpoint = "endpoints" + KubernetesRoleNode KubernetesRole = "node" + KubernetesRolePod KubernetesRole = "pod" + KubernetesRoleService KubernetesRole = "service" + KubernetesRoleEndpoint KubernetesRole = "endpoints" + KubernetesRoleIngress KubernetesRole = "ingress" ) // UnmarshalYAML implements the yaml.Unmarshaler interface. @@ -1017,7 +1018,7 @@ func (c *KubernetesRole) UnmarshalYAML(unmarshal func(interface{}) error) error return err } switch *c { - case KubernetesRoleNode, KubernetesRolePod, KubernetesRoleService, KubernetesRoleEndpoint: + case KubernetesRoleNode, KubernetesRolePod, KubernetesRoleService, KubernetesRoleEndpoint, KubernetesRoleIngress: return nil default: return fmt.Errorf("Unknown Kubernetes SD role %q", *c) diff --git a/discovery/kubernetes/endpoints.go b/discovery/kubernetes/endpoints.go index 1454cba278..b848a47436 100644 --- a/discovery/kubernetes/endpoints.go +++ b/discovery/kubernetes/endpoints.go @@ -154,18 +154,19 @@ func (e *Endpoints) Run(ctx context.Context, ch chan<- []*config.TargetGroup) { } func convertToEndpoints(o interface{}) (*apiv1.Endpoints, error) { - endpoints, isEndpoints := o.(*apiv1.Endpoints) - if !isEndpoints { - deletedState, ok := o.(cache.DeletedFinalStateUnknown) - if !ok { - return nil, fmt.Errorf("Received unexpected object: %v", o) - } - endpoints, ok = deletedState.Obj.(*apiv1.Endpoints) - if !ok { - return nil, fmt.Errorf("DeletedFinalStateUnknown contained non-Endpoints object: %v", deletedState.Obj) - } + endpoints, ok := o.(*apiv1.Endpoints) + if ok { + return endpoints, nil } + deletedState, ok := o.(cache.DeletedFinalStateUnknown) + if !ok { + return nil, fmt.Errorf("Received unexpected object: %v", o) + } + endpoints, ok = deletedState.Obj.(*apiv1.Endpoints) + if !ok { + return nil, fmt.Errorf("DeletedFinalStateUnknown contained non-Endpoints object: %v", deletedState.Obj) + } return endpoints, nil } diff --git a/discovery/kubernetes/ingress.go b/discovery/kubernetes/ingress.go new file mode 100644 index 0000000000..91dce2b5b2 --- /dev/null +++ b/discovery/kubernetes/ingress.go @@ -0,0 +1,184 @@ +// Copyright 2016 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 kubernetes + +import ( + "fmt" + + "github.com/prometheus/common/log" + "github.com/prometheus/common/model" + "github.com/prometheus/prometheus/config" + "github.com/prometheus/prometheus/util/strutil" + "golang.org/x/net/context" + "k8s.io/client-go/pkg/apis/extensions/v1beta1" + "k8s.io/client-go/tools/cache" +) + +// Ingress implements discovery of Kubernetes ingresss. +type Ingress struct { + logger log.Logger + informer cache.SharedInformer + store cache.Store +} + +// NewIngress returns a new ingress discovery. +func NewIngress(l log.Logger, inf cache.SharedInformer) *Ingress { + return &Ingress{logger: l, informer: inf, store: inf.GetStore()} +} + +// Run implements the TargetProvider interface. +func (s *Ingress) Run(ctx context.Context, ch chan<- []*config.TargetGroup) { + // Send full initial set of pod targets. + var initial []*config.TargetGroup + for _, o := range s.store.List() { + tg := s.buildIngress(o.(*v1beta1.Ingress)) + initial = append(initial, tg) + } + select { + case <-ctx.Done(): + return + case ch <- initial: + } + + // Send target groups for ingress updates. + send := func(tg *config.TargetGroup) { + select { + case <-ctx.Done(): + case ch <- []*config.TargetGroup{tg}: + } + } + s.informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: func(o interface{}) { + eventCount.WithLabelValues("ingress", "add").Inc() + + ingress, err := convertToIngress(o) + if err != nil { + s.logger.With("err", err).Errorln("converting to Ingress object failed") + return + } + send(s.buildIngress(ingress)) + }, + DeleteFunc: func(o interface{}) { + eventCount.WithLabelValues("ingress", "delete").Inc() + + ingress, err := convertToIngress(o) + if err != nil { + s.logger.With("err", err).Errorln("converting to Ingress object failed") + return + } + send(&config.TargetGroup{Source: ingressSource(ingress)}) + }, + UpdateFunc: func(_, o interface{}) { + eventCount.WithLabelValues("ingress", "update").Inc() + + ingress, err := convertToIngress(o) + if err != nil { + s.logger.With("err", err).Errorln("converting to Ingress object failed") + return + } + send(s.buildIngress(ingress)) + }, + }) + + // Block until the target provider is explicitly canceled. + <-ctx.Done() +} + +func convertToIngress(o interface{}) (*v1beta1.Ingress, error) { + ingress, ok := o.(*v1beta1.Ingress) + if ok { + return ingress, nil + } + + deletedState, ok := o.(cache.DeletedFinalStateUnknown) + if !ok { + return nil, fmt.Errorf("Received unexpected object: %v", o) + } + ingress, ok = deletedState.Obj.(*v1beta1.Ingress) + if !ok { + return nil, fmt.Errorf("DeletedFinalStateUnknown contained non-Ingress object: %v", deletedState.Obj) + } + return ingress, nil +} + +func ingressSource(s *v1beta1.Ingress) string { + return "ingress/" + s.Namespace + "/" + s.Name +} + +const ( + ingressNameLabel = metaLabelPrefix + "ingress_name" + ingressLabelPrefix = metaLabelPrefix + "ingress_label_" + ingressAnnotationPrefix = metaLabelPrefix + "ingress_annotation_" + ingressSchemeLabel = metaLabelPrefix + "ingress_scheme" + ingressHostLabel = metaLabelPrefix + "ingress_host" + ingressPathLabel = metaLabelPrefix + "ingress_path" +) + +func ingressLabels(ingress *v1beta1.Ingress) model.LabelSet { + ls := make(model.LabelSet, len(ingress.Labels)+len(ingress.Annotations)+2) + ls[ingressNameLabel] = lv(ingress.Name) + ls[namespaceLabel] = lv(ingress.Namespace) + + for k, v := range ingress.Labels { + ln := strutil.SanitizeLabelName(ingressLabelPrefix + k) + ls[model.LabelName(ln)] = lv(v) + } + + for k, v := range ingress.Annotations { + ln := strutil.SanitizeLabelName(ingressAnnotationPrefix + k) + ls[model.LabelName(ln)] = lv(v) + } + return ls +} + +func pathsFromIngressRule(rv *v1beta1.IngressRuleValue) []string { + if rv.HTTP == nil { + return []string{"/"} + } + paths := make([]string, len(rv.HTTP.Paths)) + for n, p := range rv.HTTP.Paths { + path := p.Path + if path == "" { + path = "/" + } + paths[n] = path + } + return paths +} + +func (s *Ingress) buildIngress(ingress *v1beta1.Ingress) *config.TargetGroup { + tg := &config.TargetGroup{ + Source: ingressSource(ingress), + } + tg.Labels = ingressLabels(ingress) + + schema := "http" + if ingress.Spec.TLS != nil { + schema = "https" + } + for _, rule := range ingress.Spec.Rules { + paths := pathsFromIngressRule(&rule.IngressRuleValue) + + for _, path := range paths { + tg.Targets = append(tg.Targets, model.LabelSet{ + model.AddressLabel: lv(rule.Host), + ingressSchemeLabel: lv(schema), + ingressHostLabel: lv(rule.Host), + ingressPathLabel: lv(path), + }) + } + } + + return tg +} diff --git a/discovery/kubernetes/ingress_test.go b/discovery/kubernetes/ingress_test.go new file mode 100644 index 0000000000..425e3ab239 --- /dev/null +++ b/discovery/kubernetes/ingress_test.go @@ -0,0 +1,137 @@ +// Copyright 2016 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 kubernetes + +import ( + "testing" + + "github.com/prometheus/common/log" + "github.com/prometheus/common/model" + "github.com/prometheus/prometheus/config" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/pkg/apis/extensions/v1beta1" +) + +func ingressStoreKeyFunc(obj interface{}) (string, error) { + return obj.(*v1beta1.Ingress).ObjectMeta.Name, nil +} + +func newFakeIngressInformer() *fakeInformer { + return newFakeInformer(ingressStoreKeyFunc) +} + +func makeTestIngressDiscovery() (*Ingress, *fakeInformer) { + i := newFakeIngressInformer() + return NewIngress(log.Base(), i), i +} + +func makeIngress(tls []v1beta1.IngressTLS) *v1beta1.Ingress { + return &v1beta1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testingress", + Namespace: "default", + Labels: map[string]string{"testlabel": "testvalue"}, + Annotations: map[string]string{"testannotation": "testannotationvalue"}, + }, + Spec: v1beta1.IngressSpec{ + TLS: tls, + Rules: []v1beta1.IngressRule{ + { + Host: "example.com", + IngressRuleValue: v1beta1.IngressRuleValue{ + HTTP: &v1beta1.HTTPIngressRuleValue{ + Paths: []v1beta1.HTTPIngressPath{ + {Path: "/"}, + {Path: "/foo"}, + }, + }, + }, + }, + { + // No backend config, ignored + Host: "nobackend.example.com", + IngressRuleValue: v1beta1.IngressRuleValue{ + HTTP: &v1beta1.HTTPIngressRuleValue{}, + }, + }, + { + Host: "test.example.com", + IngressRuleValue: v1beta1.IngressRuleValue{ + HTTP: &v1beta1.HTTPIngressRuleValue{ + Paths: []v1beta1.HTTPIngressPath{{}}, + }, + }, + }, + }, + }, + } +} + +func expectedTargetGroups(tls bool) []*config.TargetGroup { + scheme := "http" + if tls { + scheme = "https" + } + return []*config.TargetGroup{ + { + Targets: []model.LabelSet{ + { + "__meta_kubernetes_ingress_scheme": lv(scheme), + "__meta_kubernetes_ingress_host": "example.com", + "__meta_kubernetes_ingress_path": "/", + "__address__": "example.com", + }, + { + "__meta_kubernetes_ingress_scheme": lv(scheme), + "__meta_kubernetes_ingress_host": "example.com", + "__meta_kubernetes_ingress_path": "/foo", + "__address__": "example.com", + }, + { + "__meta_kubernetes_ingress_scheme": lv(scheme), + "__meta_kubernetes_ingress_host": "test.example.com", + "__address__": "test.example.com", + "__meta_kubernetes_ingress_path": "/", + }, + }, + Labels: model.LabelSet{ + "__meta_kubernetes_ingress_name": "testingress", + "__meta_kubernetes_namespace": "default", + "__meta_kubernetes_ingress_label_testlabel": "testvalue", + "__meta_kubernetes_ingress_annotation_testannotation": "testannotationvalue", + }, + Source: "ingress/default/testingress", + }, + } +} + +func TestIngressDiscoveryInitial(t *testing.T) { + n, i := makeTestIngressDiscovery() + i.GetStore().Add(makeIngress(nil)) + + k8sDiscoveryTest{ + discovery: n, + expectedInitial: expectedTargetGroups(false), + }.Run(t) +} + +func TestIngressDiscoveryInitialTLS(t *testing.T) { + n, i := makeTestIngressDiscovery() + i.GetStore().Add(makeIngress([]v1beta1.IngressTLS{{}})) + + k8sDiscoveryTest{ + discovery: n, + expectedInitial: expectedTargetGroups(true), + }.Run(t) +} diff --git a/discovery/kubernetes/kubernetes.go b/discovery/kubernetes/kubernetes.go index 2b7208c3e3..11a0d3598b 100644 --- a/discovery/kubernetes/kubernetes.go +++ b/discovery/kubernetes/kubernetes.go @@ -28,6 +28,7 @@ import ( "k8s.io/client-go/kubernetes" "k8s.io/client-go/pkg/api" apiv1 "k8s.io/client-go/pkg/api/v1" + "k8s.io/client-go/pkg/apis/extensions" "k8s.io/client-go/rest" "k8s.io/client-go/tools/cache" ) @@ -159,6 +160,7 @@ const resyncPeriod = 10 * time.Minute // Run implements the TargetProvider interface. func (d *Discovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup) { rclient := d.client.Core().RESTClient() + reclient := d.client.Extensions().RESTClient() namespaces := d.getNamespaces() @@ -236,6 +238,26 @@ func (d *Discovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup) { }() } wg.Wait() + case "ingress": + var wg sync.WaitGroup + for _, namespace := range namespaces { + ilw := cache.NewListWatchFromClient(reclient, "ingresses", namespace, nil) + ingress := NewIngress( + d.logger.With("kubernetes_sd", "ingress"), + cache.NewSharedInformer(ilw, &extensions.Ingress{}, resyncPeriod), + ) + go ingress.informer.Run(ctx.Done()) + + for !ingress.informer.HasSynced() { + time.Sleep(100 * time.Millisecond) + } + wg.Add(1) + go func() { + defer wg.Done() + ingress.Run(ctx, ch) + }() + } + wg.Wait() case "node": nlw := cache.NewListWatchFromClient(rclient, "nodes", api.NamespaceAll, nil) node := NewNode( diff --git a/discovery/kubernetes/node.go b/discovery/kubernetes/node.go index 518307d4e6..2e6511d2e0 100644 --- a/discovery/kubernetes/node.go +++ b/discovery/kubernetes/node.go @@ -99,18 +99,19 @@ func (n *Node) Run(ctx context.Context, ch chan<- []*config.TargetGroup) { } func convertToNode(o interface{}) (*apiv1.Node, error) { - node, isNode := o.(*apiv1.Node) - if !isNode { - deletedState, ok := o.(cache.DeletedFinalStateUnknown) - if !ok { - return nil, fmt.Errorf("Received unexpected object: %v", o) - } - node, ok = deletedState.Obj.(*apiv1.Node) - if !ok { - return nil, fmt.Errorf("DeletedFinalStateUnknown contained non-Node object: %v", deletedState.Obj) - } + node, ok := o.(*apiv1.Node) + if ok { + return node, nil } + deletedState, ok := o.(cache.DeletedFinalStateUnknown) + if !ok { + return nil, fmt.Errorf("Received unexpected object: %v", o) + } + node, ok = deletedState.Obj.(*apiv1.Node) + if !ok { + return nil, fmt.Errorf("DeletedFinalStateUnknown contained non-Node object: %v", deletedState.Obj) + } return node, nil } @@ -126,7 +127,7 @@ const ( ) func nodeLabels(n *apiv1.Node) model.LabelSet { - ls := make(model.LabelSet, len(n.Labels)+len(n.Annotations)+2) + ls := make(model.LabelSet, len(n.Labels)+len(n.Annotations)+1) ls[nodeNameLabel] = lv(n.Name) diff --git a/discovery/kubernetes/pod.go b/discovery/kubernetes/pod.go index ef8ce6d046..28b0e34d52 100644 --- a/discovery/kubernetes/pod.go +++ b/discovery/kubernetes/pod.go @@ -107,18 +107,19 @@ func (p *Pod) Run(ctx context.Context, ch chan<- []*config.TargetGroup) { } func convertToPod(o interface{}) (*apiv1.Pod, error) { - pod, isPod := o.(*apiv1.Pod) - if !isPod { - deletedState, ok := o.(cache.DeletedFinalStateUnknown) - if !ok { - return nil, fmt.Errorf("Received unexpected object: %v", o) - } - pod, ok = deletedState.Obj.(*apiv1.Pod) - if !ok { - return nil, fmt.Errorf("DeletedFinalStateUnknown contained non-Pod object: %v", deletedState.Obj) - } + pod, ok := o.(*apiv1.Pod) + if ok { + return pod, nil } + deletedState, ok := o.(cache.DeletedFinalStateUnknown) + if !ok { + return nil, fmt.Errorf("Received unexpected object: %v", o) + } + pod, ok = deletedState.Obj.(*apiv1.Pod) + if !ok { + return nil, fmt.Errorf("DeletedFinalStateUnknown contained non-Pod object: %v", deletedState.Obj) + } return pod, nil } diff --git a/discovery/kubernetes/service.go b/discovery/kubernetes/service.go index 8806ba29c5..a36f728718 100644 --- a/discovery/kubernetes/service.go +++ b/discovery/kubernetes/service.go @@ -98,18 +98,18 @@ func (s *Service) Run(ctx context.Context, ch chan<- []*config.TargetGroup) { } func convertToService(o interface{}) (*apiv1.Service, error) { - service, isService := o.(*apiv1.Service) - if !isService { - deletedState, ok := o.(cache.DeletedFinalStateUnknown) - if !ok { - return nil, fmt.Errorf("Received unexpected object: %v", o) - } - service, ok = deletedState.Obj.(*apiv1.Service) - if !ok { - return nil, fmt.Errorf("DeletedFinalStateUnknown contained non-Service object: %v", deletedState.Obj) - } + service, ok := o.(*apiv1.Service) + if ok { + return service, nil + } + deletedState, ok := o.(cache.DeletedFinalStateUnknown) + if !ok { + return nil, fmt.Errorf("Received unexpected object: %v", o) + } + service, ok = deletedState.Obj.(*apiv1.Service) + if !ok { + return nil, fmt.Errorf("DeletedFinalStateUnknown contained non-Service object: %v", deletedState.Obj) } - return service, nil } @@ -129,6 +129,7 @@ func serviceLabels(svc *apiv1.Service) model.LabelSet { ls := make(model.LabelSet, len(svc.Labels)+len(svc.Annotations)+2) ls[serviceNameLabel] = lv(svc.Name) + ls[namespaceLabel] = lv(svc.Namespace) for k, v := range svc.Labels { ln := strutil.SanitizeLabelName(serviceLabelPrefix + k) @@ -147,7 +148,6 @@ func (s *Service) buildService(svc *apiv1.Service) *config.TargetGroup { Source: serviceSource(svc), } tg.Labels = serviceLabels(svc) - tg.Labels[namespaceLabel] = lv(svc.Namespace) for _, port := range svc.Spec.Ports { addr := net.JoinHostPort(svc.Name+"."+svc.Namespace+".svc", strconv.FormatInt(int64(port.Port), 10)) diff --git a/documentation/examples/prometheus-kubernetes.yml b/documentation/examples/prometheus-kubernetes.yml index a6b9bb0fbc..3768ee8018 100644 --- a/documentation/examples/prometheus-kubernetes.yml +++ b/documentation/examples/prometheus-kubernetes.yml @@ -192,7 +192,7 @@ scrape_configs: - source_labels: [__address__] target_label: __param_target - target_label: __address__ - replacement: blackbox + replacement: blackbox-exporter.example.com:9115 - source_labels: [__param_target] target_label: instance - action: labelmap @@ -202,6 +202,40 @@ scrape_configs: - source_labels: [__meta_kubernetes_service_name] target_label: kubernetes_name +# Example scrape config for probing ingresses via the Blackbox Exporter. +# +# The relabeling allows the actual ingress scrape endpoint to be configured +# via the following annotations: +# +# * `prometheus.io/probe`: Only probe services that have a value of `true` +- job_name: 'kubernetes-ingresses' + + metrics_path: /probe + params: + module: [http_2xx] + + kubernetes_sd_configs: + - role: ingress + + relabel_configs: + - source_labels: [__meta_kubernetes_ingress_annotation_prometheus_io_probe] + action: keep + regex: true + - source_labels: [__meta_kubernetes_ingress_scheme,__address__,__meta_kubernetes_ingress_path] + regex: (.+);(.+);(.+) + replacement: ${1}://${2}${3} + target_label: __param_target + - target_label: __address__ + replacement: blackbox-exporter.example.com:9115 + - source_labels: [__param_target] + target_label: instance + - action: labelmap + regex: __meta_kubernetes_ingress_label_(.+) + - source_labels: [__meta_kubernetes_namespace] + target_label: kubernetes_namespace + - source_labels: [__meta_kubernetes_ingress_name] + target_label: kubernetes_name + # Example scrape config for pods # # The relabeling allows the actual pod scrape endpoint to be configured via the From 75aec7d9708d40243089fd26863298a6db09e212 Mon Sep 17 00:00:00 2001 From: Johannes 'fish' Ziemke Date: Wed, 6 Sep 2017 12:47:03 +0200 Subject: [PATCH 2/6] k8s: Use versioned struct for ingress discovery --- discovery/kubernetes/kubernetes.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/discovery/kubernetes/kubernetes.go b/discovery/kubernetes/kubernetes.go index 11a0d3598b..cf55e73f34 100644 --- a/discovery/kubernetes/kubernetes.go +++ b/discovery/kubernetes/kubernetes.go @@ -28,7 +28,7 @@ import ( "k8s.io/client-go/kubernetes" "k8s.io/client-go/pkg/api" apiv1 "k8s.io/client-go/pkg/api/v1" - "k8s.io/client-go/pkg/apis/extensions" + extensionsv1beta1 "k8s.io/client-go/pkg/apis/extensions/v1beta1" "k8s.io/client-go/rest" "k8s.io/client-go/tools/cache" ) @@ -244,7 +244,7 @@ func (d *Discovery) Run(ctx context.Context, ch chan<- []*config.TargetGroup) { ilw := cache.NewListWatchFromClient(reclient, "ingresses", namespace, nil) ingress := NewIngress( d.logger.With("kubernetes_sd", "ingress"), - cache.NewSharedInformer(ilw, &extensions.Ingress{}, resyncPeriod), + cache.NewSharedInformer(ilw, &extensionsv1beta1.Ingress{}, resyncPeriod), ) go ingress.informer.Run(ctx.Done()) From 7e6c6020ffc593f1c4c907cb23f6c57886e6187a Mon Sep 17 00:00:00 2001 From: wangguoliang Date: Thu, 7 Sep 2017 18:00:45 +0800 Subject: [PATCH 3/6] should use time.Since instead of time.Now().Sub Signed-off-by: wgliang --- storage/local/series.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/local/series.go b/storage/local/series.go index fe30e9f77c..f58371746a 100644 --- a/storage/local/series.go +++ b/storage/local/series.go @@ -255,7 +255,7 @@ func (s *memorySeries) maybeCloseHeadChunk(timeout time.Duration) (bool, error) if s.headChunkClosed { return false, nil } - if time.Now().Sub(s.lastTime.Time()) > timeout { + if time.Since(s.lastTime.Time()) > timeout { s.headChunkClosed = true // Since we cannot modify the head chunk from now on, we // don't need to bother with cloning anymore. From 27bdddbf512af92394711fd106247b699f43b114 Mon Sep 17 00:00:00 2001 From: Fabian Reinartz Date: Thu, 7 Sep 2017 16:24:12 +0200 Subject: [PATCH 4/6] web: fix profile paths --- web/web.go | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/web/web.go b/web/web.go index 23d5d97337..1c6d7fb954 100644 --- a/web/web.go +++ b/web/web.go @@ -246,19 +246,23 @@ func serveDebug(w http.ResponseWriter, req *http.Request) { ctx := req.Context() subpath := route.Param(ctx, "subpath") - // Based off paths from init() in golang.org/src/net/http/pprof/pprof.go - if subpath == "/pprof/" { - pprof.Index(w, req) - } else if subpath == "/pprof/cmdline" { - pprof.Cmdline(w, req) - } else if subpath == "/pprof/profile" { - pprof.Profile(w, req) - } else if subpath == "/pprof/symbol" { - pprof.Symbol(w, req) - } else if subpath == "/pprof/trace" { - pprof.Trace(w, req) - } else { + if !strings.HasPrefix(subpath, "/pprof/") { http.NotFound(w, req) + return + } + subpath = strings.TrimPrefix(subpath, "/pprof/") + + switch subpath { + case "cmdline": + pprof.Cmdline(w, req) + case "profile": + pprof.Profile(w, req) + case "symbol": + pprof.Symbol(w, req) + case "trace": + pprof.Trace(w, req) + default: + pprof.Index(w, req) } } From bb853abf24982af4f27b12f5dc69236ed7a569d1 Mon Sep 17 00:00:00 2001 From: Davor Kapsa Date: Thu, 7 Sep 2017 17:24:02 +0200 Subject: [PATCH 5/6] travis: add 1.x to go versions --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index bdb820c0f6..594894877e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ language: go go: - 1.8.x +- 1.x go_import_path: github.com/prometheus/prometheus From 7a135e0a1b91053f892d7579ed5b734971cfcee0 Mon Sep 17 00:00:00 2001 From: Jamie Moore Date: Tue, 4 Jul 2017 16:40:55 +1000 Subject: [PATCH 6/6] Add the ability to assume a role for ec2 discovery --- config/config.go | 1 + discovery/ec2/ec2.go | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index 21134e1b99..18eba3a5d3 100644 --- a/config/config.go +++ b/config/config.go @@ -1137,6 +1137,7 @@ type EC2SDConfig struct { AccessKey string `yaml:"access_key,omitempty"` SecretKey Secret `yaml:"secret_key,omitempty"` Profile string `yaml:"profile,omitempty"` + RoleARN string `yaml:"role_arn,omitempty"` RefreshInterval model.Duration `yaml:"refresh_interval,omitempty"` Port int `yaml:"port"` diff --git a/discovery/ec2/ec2.go b/discovery/ec2/ec2.go index 99cee5be3d..b96cdc449f 100644 --- a/discovery/ec2/ec2.go +++ b/discovery/ec2/ec2.go @@ -21,6 +21,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/credentials/stscreds" "github.com/aws/aws-sdk-go/aws/session" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/log" @@ -71,6 +72,7 @@ type Discovery struct { aws *aws.Config interval time.Duration profile string + roleARN string port int logger log.Logger } @@ -87,6 +89,7 @@ func NewDiscovery(conf *config.EC2SDConfig, logger log.Logger) *Discovery { Credentials: creds, }, profile: conf.Profile, + roleARN: conf.RoleARN, interval: time.Duration(conf.RefreshInterval), port: conf.Port, logger: logger, @@ -147,7 +150,13 @@ func (d *Discovery) refresh() (tg *config.TargetGroup, err error) { return nil, fmt.Errorf("could not create aws session: %s", err) } - ec2s := ec2.New(sess) + var ec2s *ec2.EC2 + if d.roleARN != "" { + creds := stscreds.NewCredentials(sess, d.roleARN) + ec2s = ec2.New(sess, &aws.Config{Credentials: creds}) + } else { + ec2s = ec2.New(sess) + } tg = &config.TargetGroup{ Source: *d.aws.Region, }