From ac45e9da025ee1ccf38aa17eb630f28c66fc4a19 Mon Sep 17 00:00:00 2001 From: Tom Wilkie Date: Fri, 29 Apr 2016 14:52:26 +0100 Subject: [PATCH] Index Pods by UID and join with containers based on this. --- probe/kubernetes/controls.go | 14 +++++++------- probe/kubernetes/pod.go | 34 ++++++++++++++++------------------ probe/kubernetes/reporter.go | 25 ++++++------------------- probe/kubernetes/service.go | 5 +++++ render/pod.go | 36 ++++++++++++++---------------------- report/id.go | 12 ++++++------ 6 files changed, 54 insertions(+), 72 deletions(-) diff --git a/probe/kubernetes/controls.go b/probe/kubernetes/controls.go index df139bf422..86af031d8a 100644 --- a/probe/kubernetes/controls.go +++ b/probe/kubernetes/controls.go @@ -16,8 +16,8 @@ const ( ) // GetLogs is the control to get the logs for a kubernetes pod -func (r *Reporter) GetLogs(req xfer.Request, namespaceID, podID string) xfer.Response { - readCloser, err := r.client.GetLogs(namespaceID, podID) +func (r *Reporter) GetLogs(req xfer.Request, uid string) xfer.Response { + readCloser, err := r.client.GetLogs(uid, uid) if err != nil { return xfer.ResponseError(err) } @@ -41,8 +41,8 @@ func (r *Reporter) GetLogs(req xfer.Request, namespaceID, podID string) xfer.Res } } -func (r *Reporter) deletePod(req xfer.Request, namespaceID, podID string) xfer.Response { - if err := r.client.DeletePod(namespaceID, podID); err != nil { +func (r *Reporter) deletePod(req xfer.Request, uid string) xfer.Response { + if err := r.client.DeletePod(uid, uid); err != nil { return xfer.ResponseError(err) } return xfer.Response{ @@ -51,13 +51,13 @@ func (r *Reporter) deletePod(req xfer.Request, namespaceID, podID string) xfer.R } // CapturePod is exported for testing -func CapturePod(f func(xfer.Request, string, string) xfer.Response) func(xfer.Request) xfer.Response { +func CapturePod(f func(xfer.Request, string) xfer.Response) func(xfer.Request) xfer.Response { return func(req xfer.Request) xfer.Response { - namespaceID, podID, ok := report.ParsePodNodeID(req.NodeID) + uid, ok := report.ParsePodNodeID(req.NodeID) if !ok { return xfer.ResponseErrorf("Invalid ID: %s", req.NodeID) } - return f(req, namespaceID, podID) + return f(req, uid) } } diff --git a/probe/kubernetes/pod.go b/probe/kubernetes/pod.go index badea86c7a..fb4550f4aa 100644 --- a/probe/kubernetes/pod.go +++ b/probe/kubernetes/pod.go @@ -11,23 +11,22 @@ import ( // These constants are keys used in node metadata const ( - PodID = "kubernetes_pod_id" - PodName = "kubernetes_pod_name" - PodCreated = "kubernetes_pod_created" - PodContainerIDs = "kubernetes_pod_container_ids" - PodState = "kubernetes_pod_state" - PodLabelPrefix = "kubernetes_pod_labels_" - ServiceIDs = "kubernetes_service_ids" + PodID = "kubernetes_pod_id" + PodName = "kubernetes_pod_name" + PodCreated = "kubernetes_pod_created" + PodState = "kubernetes_pod_state" + PodLabelPrefix = "kubernetes_pod_labels_" + ServiceIDs = "kubernetes_service_ids" StateDeleted = "deleted" ) // Pod represents a Kubernetes pod type Pod interface { + UID() string ID() string Name() string Namespace() string - ContainerIDs() []string Created() string AddServiceID(id string) Labels() labels.Labels @@ -46,6 +45,14 @@ func NewPod(p *api.Pod) Pod { return &pod{Pod: p} } +func (p *pod) UID() string { + // Work around for master pod not reporting the right UID. + if hash, ok := p.ObjectMeta.Annotations["kubernetes.io/config.hash"]; ok { + return hash + } + return string(p.ObjectMeta.UID) +} + func (p *pod) ID() string { return p.ObjectMeta.Namespace + "/" + p.ObjectMeta.Name } @@ -62,14 +69,6 @@ func (p *pod) Created() string { return p.ObjectMeta.CreationTimestamp.Format(time.RFC822) } -func (p *pod) ContainerIDs() []string { - ids := []string{} - for _, container := range p.Status.ContainerStatuses { - ids = append(ids, strings.TrimPrefix(container.ContainerID, "docker://")) - } - return ids -} - func (p *pod) Labels() labels.Labels { return labels.Set(p.ObjectMeta.Labels) } @@ -87,12 +86,11 @@ func (p *pod) NodeName() string { } func (p *pod) GetNode(probeID string) report.Node { - n := report.MakeNodeWith(report.MakePodNodeID(p.Namespace(), p.Name()), map[string]string{ + n := report.MakeNodeWith(report.MakePodNodeID(p.UID()), map[string]string{ PodID: p.ID(), PodName: p.Name(), Namespace: p.Namespace(), PodCreated: p.Created(), - PodContainerIDs: strings.Join(p.ContainerIDs(), " "), PodState: p.State(), report.ControlProbeID: probeID, }) diff --git a/probe/kubernetes/reporter.go b/probe/kubernetes/reporter.go index ad47c05305..d7db18788c 100644 --- a/probe/kubernetes/reporter.go +++ b/probe/kubernetes/reporter.go @@ -10,7 +10,6 @@ import ( "github.com/weaveworks/scope/probe" "github.com/weaveworks/scope/probe/controls" - "github.com/weaveworks/scope/probe/docker" "github.com/weaveworks/scope/report" ) @@ -81,7 +80,7 @@ func (r *Reporter) podEvent(e Event, pod Pod) { rpt.Shortcut = true rpt.Pod.AddNode( report.MakeNodeWith( - report.MakePodNodeID(pod.Namespace(), pod.Name()), + report.MakePodNodeID(pod.UID()), map[string]string{PodState: StateDeleted}, ), ) @@ -96,13 +95,12 @@ func (r *Reporter) Report() (report.Report, error) { if err != nil { return result, err } - podTopology, containerTopology, err := r.podTopology(services) + podTopology, err := r.podTopology(services) if err != nil { return result, err } result.Service = result.Service.Merge(serviceTopology) result.Pod = result.Pod.Merge(podTopology) - result.Container = result.Container.Merge(containerTopology) return result, nil } @@ -142,13 +140,12 @@ var GetNodeName = func(r *Reporter) (string, error) { return nodeName, err } -func (r *Reporter) podTopology(services []Service) (report.Topology, report.Topology, error) { +func (r *Reporter) podTopology(services []Service) (report.Topology, error) { var ( pods = report.MakeTopology(). WithMetadataTemplates(PodMetadataTemplates). WithTableTemplates(PodTableTemplates) - containers = report.MakeTopology() - selectors = map[string]labels.Selector{} + selectors = map[string]labels.Selector{} ) pods.Controls.AddControl(report.Control{ ID: GetLogs, @@ -168,7 +165,7 @@ func (r *Reporter) podTopology(services []Service) (report.Topology, report.Topo thisNodeName, err := GetNodeName(r) if err != nil { - return pods, containers, err + return pods, err } err = r.client.WalkPods(func(p Pod) error { if p.NodeName() != thisNodeName { @@ -179,18 +176,8 @@ func (r *Reporter) podTopology(services []Service) (report.Topology, report.Topo p.AddServiceID(serviceID) } } - nodeID := report.MakePodNodeID(p.Namespace(), p.Name()) pods = pods.AddNode(p.GetNode(r.probeID)) - - for _, containerID := range p.ContainerIDs() { - container := report.MakeNodeWith(report.MakeContainerNodeID(containerID), map[string]string{ - PodID: p.ID(), - Namespace: p.Namespace(), - docker.ContainerID: containerID, - }).WithParents(report.EmptySets.Add(report.Pod, report.MakeStringSet(nodeID))) - containers.AddNode(container) - } return nil }) - return pods, containers, err + return pods, err } diff --git a/probe/kubernetes/service.go b/probe/kubernetes/service.go index 99886303d9..c5ca38a3f8 100644 --- a/probe/kubernetes/service.go +++ b/probe/kubernetes/service.go @@ -20,6 +20,7 @@ const ( // Service represents a Kubernetes service type Service interface { + UID() string ID() string Name() string Namespace() string @@ -36,6 +37,10 @@ func NewService(s *api.Service) Service { return &service{Service: s} } +func (s *service) UID() string { + return string(s.ObjectMeta.UID) +} + func (s *service) ID() string { return s.ObjectMeta.Namespace + "/" + s.ObjectMeta.Name } diff --git a/render/pod.go b/render/pod.go index c3cc7e77fa..fdb1fda371 100644 --- a/render/pod.go +++ b/render/pod.go @@ -3,6 +3,7 @@ package render import ( "strings" + "github.com/weaveworks/scope/probe/docker" "github.com/weaveworks/scope/probe/kubernetes" "github.com/weaveworks/scope/report" ) @@ -61,7 +62,7 @@ var PodServiceRenderer = FilterEmpty(report.Pod, // It does not have enough info to do that, and the resulting graph // must be merged with a container graph to get that info. func MapContainer2Pod(n report.Node, _ report.Networks) report.Nodes { - // Uncontainerd becomes unmanaged in the pods view + // Uncontained becomes unmanaged in the pods view if strings.HasPrefix(n.ID, MakePseudoNodeID(UncontainedID)) { id := MakePseudoNodeID(UnmanagedID, report.ExtractHostID(n)) node := NewDerivedPseudoNode(id, n) @@ -73,34 +74,25 @@ func MapContainer2Pod(n report.Node, _ report.Networks) report.Nodes { return report.Nodes{n.ID: n} } - // Otherwise, if some some reason the container doesn't have a pod_id (maybe - // slightly out of sync reports, or its not in a pod), just drop it - namespace, ok := n.Latest.Lookup(kubernetes.Namespace) - if !ok { - id := MakePseudoNodeID(UnmanagedID, report.ExtractHostID(n)) - node := NewDerivedPseudoNode(id, n) - return report.Nodes{id: node} + // Ignore non-running containers + if state, ok := n.Latest.Lookup(docker.ContainerState); ok && state != docker.StateRunning { + return report.Nodes{} } - podID, ok := n.Latest.Lookup(kubernetes.PodID) + + // Otherwise, if some some reason the container doesn't have a pod uid (maybe + // slightly out of sync reports, or its not in a pod), make it part of unmanaged. + uid, ok := n.Latest.Lookup(docker.LabelPrefix + "io.kubernetes.pod.uid") if !ok { id := MakePseudoNodeID(UnmanagedID, report.ExtractHostID(n)) node := NewDerivedPseudoNode(id, n) return report.Nodes{id: node} } - podName := strings.TrimPrefix(podID, namespace+"/") - id := report.MakePodNodeID(namespace, podName) - // Due to a bug in kubernetes, addon pods on the master node are not returned - // from the API. Adding the namespace and pod name is a workaround until - // https://github.com/kubernetes/kubernetes/issues/14738 is fixed. - return report.Nodes{ - id: NewDerivedNode(id, n). - WithTopology(report.Pod). - WithLatests(map[string]string{ - kubernetes.Namespace: namespace, - kubernetes.PodName: podName, - }), - } + id := report.MakePodNodeID(uid) + node := NewDerivedNode(id, n). + WithTopology(report.Pod) + node.Counters = node.Counters.Add(n.Topology, 1) + return report.Nodes{id: node} } // MapPod2Service maps pod Nodes to service Nodes. diff --git a/report/id.go b/report/id.go index 2ffc0b2a3a..899ff427c3 100644 --- a/report/id.go +++ b/report/id.go @@ -112,8 +112,8 @@ func MakeContainerImageNodeID(containerImageID string) string { } // MakePodNodeID produces a pod node ID from its composite parts. -func MakePodNodeID(namespaceID, podID string) string { - return namespaceID + ScopeDelim + podID +func MakePodNodeID(uid string) string { + return uid + ScopeDelim + "" } // MakeServiceNodeID produces a service node ID from its composite parts. @@ -167,12 +167,12 @@ func ParseAddressNodeID(addressNodeID string) (hostID, address string, ok bool) } // ParsePodNodeID produces the namespace ID and pod ID from an pod node ID. -func ParsePodNodeID(podNodeID string) (namespaceID, podID string, ok bool) { +func ParsePodNodeID(podNodeID string) (uid string, ok bool) { fields := strings.SplitN(podNodeID, ScopeDelim, 2) - if len(fields) != 2 { - return "", "", false + if len(fields) != 2 || fields[1] != "" { + return "", false } - return fields[0], fields[1], true + return fields[0], true } // ExtractHostID extracts the host id from Node