Index services by UID, and refactor out common k8s metadata handling

This commit is contained in:
Paul Bellamy
2016-05-04 17:02:48 +01:00
parent bb284edee8
commit 0178babb0e
7 changed files with 76 additions and 130 deletions

View File

@@ -1,12 +1,8 @@
package kubernetes
import (
"strings"
"time"
"github.com/weaveworks/scope/report"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/labels"
)
// These constants are keys used in node metadata
@@ -24,26 +20,22 @@ const (
// Pod represents a Kubernetes pod
type Pod interface {
UID() string
ID() string
Name() string
Namespace() string
Created() string
Meta
AddServiceID(id string)
Labels() labels.Labels
NodeName() string
GetNode(probeID string) report.Node
}
type pod struct {
*api.Pod
Meta
serviceIDs report.StringSet
Node *api.Node
}
// NewPod creates a new Pod
func NewPod(p *api.Pod) Pod {
return &pod{Pod: p, serviceIDs: report.MakeStringSet()}
return &pod{Pod: p, Meta: meta{p.ObjectMeta}, serviceIDs: report.MakeStringSet()}
}
func (p *pod) UID() string {
@@ -51,27 +43,7 @@ func (p *pod) UID() string {
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
}
func (p *pod) Name() string {
return p.ObjectMeta.Name
}
func (p *pod) Namespace() string {
return p.ObjectMeta.Namespace
}
func (p *pod) Created() string {
return p.ObjectMeta.CreationTimestamp.Format(time.RFC822)
}
func (p *pod) Labels() labels.Labels {
return labels.Set(p.ObjectMeta.Labels)
return p.Meta.UID()
}
func (p *pod) AddServiceID(id string) {
@@ -97,12 +69,8 @@ func (p *pod) GetNode(probeID string) report.Node {
report.ControlProbeID: probeID,
}).WithSets(report.EmptySets.Add(ServiceIDs, p.serviceIDs))
for _, serviceID := range p.serviceIDs {
segments := strings.SplitN(serviceID, "/", 2)
if len(segments) != 2 {
continue
}
n = n.WithParents(report.EmptySets.
Add(report.Service, report.MakeStringSet(report.MakeServiceNodeID(p.Namespace(), segments[1]))),
Add(report.Service, report.MakeStringSet(report.MakeServiceNodeID(serviceID))),
)
}
n = n.AddTable(PodLabelPrefix, p.ObjectMeta.Labels)

View File

@@ -192,7 +192,14 @@ func (r *Reporter) podTopology(services []Service) (report.Topology, error) {
pods = report.MakeTopology().
WithMetadataTemplates(PodMetadataTemplates).
WithTableTemplates(PodTableTemplates)
selectors = map[string]labels.Selector{}
selectors = []func(Pod){}
match = func(selector labels.Selector, f func(Pod, string), id string) func(Pod) {
return func(p Pod) {
if selector.Matches(p.Labels()) {
f(p, id)
}
}
}
)
pods.Controls.AddControl(report.Control{
ID: GetLogs,
@@ -207,7 +214,7 @@ func (r *Reporter) podTopology(services []Service) (report.Topology, error) {
Rank: 1,
})
for _, service := range services {
selectors[service.ID()] = service.Selector()
selectors = append(selectors, match(service.Selector(), Pod.AddServiceID, service.UID()))
}
thisNodeName, err := GetNodeName(r)
@@ -218,10 +225,8 @@ func (r *Reporter) podTopology(services []Service) (report.Topology, error) {
if p.NodeName() != thisNodeName {
return nil
}
for serviceID, selector := range selectors {
if selector.Matches(p.Labels()) {
p.AddServiceID(serviceID)
}
for _, selector := range selectors {
selector(p)
}
pods = pods.AddNode(p.GetNode(r.probeID))
return nil

View File

@@ -22,6 +22,7 @@ var (
nodeName = "nodename"
pod1UID = "a1b2c3d4e5"
pod2UID = "f6g7h8i9j0"
serviceUID = "service1234"
podTypeMeta = unversioned.TypeMeta{
Kind: "Pod",
APIVersion: "v1",
@@ -73,6 +74,7 @@ var (
},
ObjectMeta: api.ObjectMeta{
Name: "pongservice",
UID: types.UID(serviceUID),
Namespace: "ping",
CreationTimestamp: unversioned.Now(),
},
@@ -166,7 +168,7 @@ func TestReporter(t *testing.T) {
pod1ID := report.MakePodNodeID(pod1UID)
pod2ID := report.MakePodNodeID(pod2UID)
serviceID := report.MakeServiceNodeID("ping", "pongservice")
serviceID := report.MakeServiceNodeID(serviceUID)
rpt, _ := kubernetes.NewReporter(newMockClient(), nil, "", nil).Report()
// Reporter should have added the following pods
@@ -182,7 +184,7 @@ func TestReporter(t *testing.T) {
kubernetes.Namespace: "ping",
kubernetes.PodCreated: pod1.Created(),
}, map[string]report.StringSet{
kubernetes.ServiceIDs: report.MakeStringSet("ping/pongservice"),
kubernetes.ServiceIDs: report.MakeStringSet(serviceUID),
}},
{pod2ID, serviceID, map[string]string{
kubernetes.PodID: "ping/pong-b",
@@ -190,7 +192,7 @@ func TestReporter(t *testing.T) {
kubernetes.Namespace: "ping",
kubernetes.PodCreated: pod1.Created(),
}, map[string]report.StringSet{
kubernetes.ServiceIDs: report.MakeStringSet("ping/pongservice"),
kubernetes.ServiceIDs: report.MakeStringSet(serviceUID),
}},
} {
node, ok := rpt.Pod.Nodes[pod.id]

View File

@@ -1,8 +1,6 @@
package kubernetes
import (
"time"
"github.com/weaveworks/scope/report"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/labels"
@@ -20,37 +18,19 @@ const (
// Service represents a Kubernetes service
type Service interface {
UID() string
ID() string
Name() string
Namespace() string
Meta
GetNode() report.Node
Selector() labels.Selector
}
type service struct {
*api.Service
Meta
}
// NewService creates a new Service
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
}
func (s *service) Name() string {
return s.ObjectMeta.Name
}
func (s *service) Namespace() string {
return s.ObjectMeta.Namespace
return &service{Service: s, Meta: meta{s.ObjectMeta}}
}
func (s *service) Selector() labels.Selector {
@@ -64,7 +44,7 @@ func (s *service) GetNode() report.Node {
latest := map[string]string{
ServiceID: s.ID(),
ServiceName: s.Name(),
ServiceCreated: s.ObjectMeta.CreationTimestamp.Format(time.RFC822),
ServiceCreated: s.Created(),
Namespace: s.Namespace(),
ServiceIP: s.Spec.ClusterIP,
}
@@ -72,8 +52,8 @@ func (s *service) GetNode() report.Node {
latest[ServicePublicIP] = s.Spec.LoadBalancerIP
}
return report.MakeNodeWith(
report.MakeServiceNodeID(s.Namespace(), s.Name()),
report.MakeServiceNodeID(s.UID()),
latest,
).
AddTable(ServiceLabelPrefix, s.Labels)
AddTable(ServiceLabelPrefix, s.ObjectMeta.Labels)
}

View File

@@ -95,40 +95,34 @@ func MapContainer2Pod(n report.Node, _ report.Networks) report.Nodes {
return report.Nodes{id: node}
}
// MapPod2Service maps pod Nodes to service Nodes.
//
// If this function is given a node without a kubernetes_pod_id
// (including other pseudo nodes), it will produce an "Uncontained"
// pseudo node.
//
// Otherwise, this function will produce a node with the correct ID
// format for a container, but without any Major or Minor labels.
// It does not have enough info to do that, and the resulting graph
// must be merged with a pod graph to get that info.
func MapPod2Service(pod report.Node, _ report.Networks) report.Nodes {
// Propagate all pseudo nodes
if pod.Topology == Pseudo {
return report.Nodes{pod.ID: pod}
}
// The various ways of grouping pods
var (
MapPod2Service = MapPod2Grouping(report.Service, kubernetes.ServiceIDs, report.MakeServiceNodeID)
)
// Otherwise, if some some reason the pod doesn't have a service_ids (maybe
// slightly out of sync reports, or its not in a service), just drop it
namespace, ok := pod.Latest.Lookup(kubernetes.Namespace)
if !ok {
return report.Nodes{}
}
serviceIDs, ok := pod.Sets.Lookup(kubernetes.ServiceIDs)
if !ok {
return report.Nodes{}
}
// MapPod2Grouping maps pod Nodes to some kubernetes grouping.
func MapPod2Grouping(topology, setKey string, idMaker func(uid string) string) func(pod report.Node, _ report.Networks) report.Nodes {
return func(pod report.Node, _ report.Networks) report.Nodes {
// Propagate all pseudo nodes
if pod.Topology == Pseudo {
return report.Nodes{pod.ID: pod}
}
result := report.Nodes{}
for _, serviceID := range serviceIDs {
serviceName := strings.TrimPrefix(serviceID, namespace+"/")
id := report.MakeServiceNodeID(namespace, serviceName)
node := NewDerivedNode(id, pod).WithTopology(report.Service)
node.Counters = node.Counters.Add(pod.Topology, 1)
result[id] = node
// Otherwise, if some some reason the pod doesn't have any of these ids
// (maybe slightly out of sync reports, or its not in this group), just
// drop it
groupIDs, ok := pod.Sets.Lookup(setKey)
if !ok {
return report.Nodes{}
}
result := report.Nodes{}
for _, groupID := range groupIDs {
id := idMaker(groupID)
node := NewDerivedNode(id, pod).WithTopology(topology)
node.Counters = node.Counters.Add(pod.Topology, 1)
result[id] = node
}
return result
}
return result
}

View File

@@ -96,32 +96,28 @@ func MakeProcessNodeID(hostID, pid string) string {
return hostID + ScopeDelim + pid
}
// MakeHostNodeID produces a host node ID from its composite parts.
func MakeHostNodeID(hostID string) string {
// hostIDs come from the probe and are presumed to be globally-unique.
// But, suffix something to elicit failures if we try to use probe host
// IDs directly as node IDs in the host topology.
return hostID + ScopeDelim + "<host>"
}
var (
// MakeHostNodeID produces a host node ID from its composite parts.
MakeHostNodeID = singleComponentID("host")
// MakeContainerNodeID produces a container node ID from its composite parts.
func MakeContainerNodeID(containerID string) string {
return containerID + ScopeDelim + "<container>"
}
// MakeContainerNodeID produces a container node ID from its composite parts.
MakeContainerNodeID = singleComponentID("container")
// MakeContainerImageNodeID produces a container image node ID from its composite parts.
func MakeContainerImageNodeID(containerImageID string) string {
return containerImageID + ScopeDelim + "<container_image>"
}
// MakeContainerImageNodeID produces a container image node ID from its composite parts.
MakeContainerImageNodeID = singleComponentID("container_image")
// MakePodNodeID produces a pod node ID from its composite parts.
func MakePodNodeID(uid string) string {
return uid + ScopeDelim + "<pod>"
}
// MakePodNodeID produces a pod node ID from its composite parts.
MakePodNodeID = singleComponentID("pod")
// MakeServiceNodeID produces a service node ID from its composite parts.
func MakeServiceNodeID(namespaceID, serviceID string) string {
return namespaceID + ScopeDelim + serviceID
// MakeServiceNodeID produces a service node ID from its composite parts.
MakeServiceNodeID = singleComponentID("service")
)
// singleComponentID makes a
func singleComponentID(tag string) func(string) string {
return func(id string) string {
return id + ScopeDelim + "<" + tag + ">"
}
}
// MakeOverlayNodeID produces an overlay topology node ID from a router peer's

View File

@@ -97,7 +97,8 @@ var (
ClientPodNodeID = report.MakePodNodeID(ClientPodUID)
ServerPodNodeID = report.MakePodNodeID(ServerPodUID)
ServiceID = "ping/pongservice"
ServiceNodeID = report.MakeServiceNodeID(KubernetesNamespace, "pongservice")
ServiceUID = "service1234"
ServiceNodeID = report.MakeServiceNodeID(ServiceUID)
ClientProcess1CPUMetric = report.MakeMetric().Add(Now, 0.01).WithFirst(Now.Add(-1 * time.Second))
ClientProcess1MemoryMetric = report.MakeMetric().Add(Now, 0.02).WithFirst(Now.Add(-2 * time.Second))
@@ -373,7 +374,7 @@ var (
report.HostNodeID: ClientHostNodeID,
}).
WithSets(report.EmptySets.
Add(kubernetes.ServiceIDs, report.MakeStringSet(ServiceID))).
Add(kubernetes.ServiceIDs, report.MakeStringSet(ServiceUID))).
WithTopology(report.Pod).WithParents(report.EmptySets.
Add("host", report.MakeStringSet(ClientHostNodeID)).
Add("service", report.MakeStringSet(ServiceID)),
@@ -387,7 +388,7 @@ var (
report.HostNodeID: ServerHostNodeID,
}).
WithSets(report.EmptySets.
Add(kubernetes.ServiceIDs, report.MakeStringSet(ServiceID))).
Add(kubernetes.ServiceIDs, report.MakeStringSet(ServiceUID))).
WithTopology(report.Pod).WithParents(report.EmptySets.
Add("host", report.MakeStringSet(ServerHostNodeID)).
Add("service", report.MakeStringSet(ServiceID)),