From 6c8080607f9480ce40ea2f078d1a38bb15968af8 Mon Sep 17 00:00:00 2001 From: Jimmi Dyson Date: Wed, 20 Jul 2016 12:00:54 +0100 Subject: [PATCH] Kubernetes SD: Add node name and host IP to pod discovery --- retrieval/discovery/kubernetes/discovery.go | 4 ++++ retrieval/discovery/kubernetes/discovery_test.go | 14 ++++++++++++++ retrieval/discovery/kubernetes/pod.go | 8 ++++++++ retrieval/discovery/kubernetes/types.go | 2 ++ 4 files changed, 28 insertions(+) diff --git a/retrieval/discovery/kubernetes/discovery.go b/retrieval/discovery/kubernetes/discovery.go index f05e2aa5e..f01c6e007 100644 --- a/retrieval/discovery/kubernetes/discovery.go +++ b/retrieval/discovery/kubernetes/discovery.go @@ -62,6 +62,10 @@ const ( podLabelPrefix = metaLabelPrefix + "pod_label_" // podAnnotationPrefix is the prefix for prom label names corresponding to k8s annotations for a target pod podAnnotationPrefix = metaLabelPrefix + "pod_annotation_" + // podNodeLabel is the name for the label containing the name of the node that a pod is scheduled on to + podNodeNameLabel = metaLabelPrefix + "pod_node_name" + // podHostIPLabel is the name for the label containing the IP of the node that a pod is scheduled on to + podHostIPLabel = metaLabelPrefix + "pod_host_ip" sourceServicePrefix = "services" // serviceNamespaceLabel is the name for the label containing a target's service namespace. diff --git a/retrieval/discovery/kubernetes/discovery_test.go b/retrieval/discovery/kubernetes/discovery_test.go index 78144791c..fc26b02b4 100644 --- a/retrieval/discovery/kubernetes/discovery_test.go +++ b/retrieval/discovery/kubernetes/discovery_test.go @@ -117,9 +117,11 @@ func pod(name string, containers []Container) *Pod { Status: "True", }, }, + HostIP: "2.2.2.2", }, PodSpec: PodSpec{ Containers: c, + NodeName: "test-node", }, } } @@ -147,6 +149,12 @@ func TestUpdatePodTargets(t *testing.T) { t.Fatalf("expected 0 targets, received %d", len(result)) } + // Return no targets for a pod that has no HostIP + result = updatePodTargets(&Pod{PodStatus: PodStatus{PodIP: "1.1.1.1", Phase: "Running"}}, true) + if len(result) > 0 { + t.Fatalf("expected 0 targets, received %d", len(result)) + } + // A pod with all valid containers should return one target per container with allContainers=true result = updatePodTargets(pod("easy", []Container{container("a", portsA), container("b", portsB)}), true) if len(result) != 2 { @@ -167,6 +175,12 @@ func TestUpdatePodTargets(t *testing.T) { if result[1][podContainerPortMapPrefix+"https"] != "443" { t.Fatalf("expected result[1][podContainerPortMapPrefix + 'https'] to be '443', but was %s", result[1][podContainerPortMapPrefix+"https"]) } + if result[0][podNodeNameLabel] != "test-node" { + t.Fatalf("expected result[0] podNodeNameLabel 'test-node', received '%s'", result[0][podNodeNameLabel]) + } + if result[0][podHostIPLabel] != "2.2.2.2" { + t.Fatalf("expected result[0] podHostIPLabel '2.2.2.2', received '%s'", result[0][podHostIPLabel]) + } // A pod with all valid containers should return one target with allContainers=false, and it should be the alphabetically first container result = updatePodTargets(pod("easy", []Container{container("a", portsA), container("b", portsB)}), false) diff --git a/retrieval/discovery/kubernetes/pod.go b/retrieval/discovery/kubernetes/pod.go index 1d9759d55..082553726 100644 --- a/retrieval/discovery/kubernetes/pod.go +++ b/retrieval/discovery/kubernetes/pod.go @@ -255,6 +255,12 @@ func updatePodTargets(pod *Pod, allContainers bool) []model.LabelSet { return targets } + // Should never hit this (running pods should always have this set), but better to be defensive. + if pod.PodStatus.HostIP == "" { + log.Debugf("skipping pod %s -- PodStatus.HostIP is empty", pod.ObjectMeta.Name) + return targets + } + ready := "unknown" for _, cond := range pod.PodStatus.Conditions { if strings.ToLower(cond.Type) == "ready" { @@ -293,6 +299,8 @@ func updatePodTargets(pod *Pod, allContainers bool) []model.LabelSet { podContainerNameLabel: model.LabelValue(container.Name), podContainerPortNameLabel: model.LabelValue(tcpPorts[0].Name), podReadyLabel: model.LabelValue(ready), + podNodeNameLabel: model.LabelValue(pod.PodSpec.NodeName), + podHostIPLabel: model.LabelValue(pod.PodStatus.HostIP), } for _, port := range tcpPorts { diff --git a/retrieval/discovery/kubernetes/types.go b/retrieval/discovery/kubernetes/types.go index 3abc51857..e77076d97 100644 --- a/retrieval/discovery/kubernetes/types.go +++ b/retrieval/discovery/kubernetes/types.go @@ -285,10 +285,12 @@ type PodStatus struct { Phase string `json:"phase" description:"Current condition of the pod. More info: http://kubernetes.io/v1.1/docs/user-guide/pod-states.html#pod-phase"` PodIP string `json:"podIP" description:"IP address allocated to the pod. Routable at least within the cluster. Empty if not yet allocated."` Conditions []PodCondition `json:"conditions" description:"Current service state of pod."` + HostIP string `json:"hostIP,omitempty" description:"IP address of the host to which the pod is assigned. Empty if not yet scheduled."` } type PodSpec struct { Containers []Container `json:"containers" description:"list of containers, see http://kubernetes.io/v1.1/docs/api-reference/v1/definitions.html#_v1_container"` + NodeName string `json:"nodeName,omitempty" description:"NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements."` } type PodCondition struct {