From 0178babb0e02434cdf1a238fc16b6a415c855f65 Mon Sep 17 00:00:00 2001 From: Paul Bellamy Date: Wed, 4 May 2016 17:02:48 +0100 Subject: [PATCH 1/4] Index services by UID, and refactor out common k8s metadata handling --- probe/kubernetes/pod.go | 42 +++------------------- probe/kubernetes/reporter.go | 17 +++++---- probe/kubernetes/reporter_test.go | 8 +++-- probe/kubernetes/service.go | 32 ++++------------- render/pod.go | 60 ++++++++++++++----------------- report/id.go | 40 ++++++++++----------- test/fixture/report_fixture.go | 7 ++-- 7 files changed, 76 insertions(+), 130 deletions(-) diff --git a/probe/kubernetes/pod.go b/probe/kubernetes/pod.go index b5a1165154..42a9c3df18 100644 --- a/probe/kubernetes/pod.go +++ b/probe/kubernetes/pod.go @@ -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) diff --git a/probe/kubernetes/reporter.go b/probe/kubernetes/reporter.go index 6a124c3156..2fabdfdcbf 100644 --- a/probe/kubernetes/reporter.go +++ b/probe/kubernetes/reporter.go @@ -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 diff --git a/probe/kubernetes/reporter_test.go b/probe/kubernetes/reporter_test.go index b7b65475d9..985b9304a8 100644 --- a/probe/kubernetes/reporter_test.go +++ b/probe/kubernetes/reporter_test.go @@ -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] diff --git a/probe/kubernetes/service.go b/probe/kubernetes/service.go index c5ca38a3f8..c47c68d234 100644 --- a/probe/kubernetes/service.go +++ b/probe/kubernetes/service.go @@ -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) } diff --git a/render/pod.go b/render/pod.go index 7d3cf09482..f8b048961b 100644 --- a/render/pod.go +++ b/render/pod.go @@ -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} + } + + // 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 _, 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 + 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 } diff --git a/report/id.go b/report/id.go index 9152286ddf..ae4ebfc136 100644 --- a/report/id.go +++ b/report/id.go @@ -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 + "" -} +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 + "" -} + // 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 + "" -} + // 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 + "" -} + // MakePodNodeID produces a pod node ID from its composite parts. + MakePodNodeID = singleComponentID("pod") + + // MakeServiceNodeID produces a service node ID from its composite parts. + MakeServiceNodeID = singleComponentID("service") +) -// MakeServiceNodeID produces a service node ID from its composite parts. -func MakeServiceNodeID(namespaceID, serviceID string) string { - return namespaceID + ScopeDelim + serviceID +// 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 diff --git a/test/fixture/report_fixture.go b/test/fixture/report_fixture.go index 84b9d358db..91ef83886f 100644 --- a/test/fixture/report_fixture.go +++ b/test/fixture/report_fixture.go @@ -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)), From 16a5c738d9a2118eb720b6948eddcf847b828097 Mon Sep 17 00:00:00 2001 From: Paul Bellamy Date: Wed, 4 May 2016 17:03:05 +0100 Subject: [PATCH 2/4] Deployment and ReplicaSet views for k8s --- app/api_topologies.go | 35 +++++--- probe/appclient/app_client_internal_test.go | 4 + probe/kubernetes/client.go | 66 ++++++++++++-- probe/kubernetes/deployment.go | 71 +++++++++++++++ probe/kubernetes/meta.go | 46 ++++++++++ probe/kubernetes/pod.go | 20 ++--- probe/kubernetes/replica_set.go | 65 ++++++++++++++ probe/kubernetes/reporter.go | 97 +++++++++++++++++++-- probe/kubernetes/reporter_test.go | 17 ++-- probe/probe_internal_test.go | 2 + render/detailed/node_test.go | 5 ++ render/detailed/summary.go | 34 ++++++++ render/pod.go | 41 +++++++-- render/selectors.go | 2 + report/id.go | 6 ++ report/report.go | 28 ++++++ test/fixture/report_fixture.go | 9 +- 17 files changed, 487 insertions(+), 61 deletions(-) create mode 100644 probe/kubernetes/deployment.go create mode 100644 probe/kubernetes/meta.go create mode 100644 probe/kubernetes/replica_set.go diff --git a/app/api_topologies.go b/app/api_topologies.go index 625cb1335d..3fb0be82bf 100644 --- a/app/api_topologies.go +++ b/app/api_topologies.go @@ -82,17 +82,17 @@ func init() { Options: containerFilters, }, APITopologyDesc{ - id: "containers-by-image", + id: "containers-by-hostname", parent: "containers", - renderer: render.ContainerImageRenderer, - Name: "by image", + renderer: render.ContainerHostnameRenderer, + Name: "by DNS name", Options: containerFilters, }, APITopologyDesc{ - id: "containers-by-hostname", + id: "containers-by-image", parent: "containers", - renderer: render.ContainerHostnameRenderer, - Name: "by DNS name", + renderer: render.ContainerImageRenderer, + Name: "by image", Options: containerFilters, }, APITopologyDesc{ @@ -103,10 +103,24 @@ func init() { HideIfEmpty: true, }, APITopologyDesc{ - id: "pods-by-service", + id: "replica-sets", + parent: "pods", + renderer: render.ReplicaSetRenderer, + Name: "replica sets", + HideIfEmpty: true, + }, + APITopologyDesc{ + id: "deployments", + parent: "pods", + renderer: render.DeploymentRenderer, + Name: "deployments", + HideIfEmpty: true, + }, + APITopologyDesc{ + id: "services", parent: "pods", renderer: render.PodServiceRenderer, - Name: "by service", + Name: "services", HideIfEmpty: true, }, APITopologyDesc{ @@ -136,7 +150,7 @@ func kubernetesFilters(namespaces ...string) APITopologyOptionGroup { // Currently only kubernetes changes. func updateFilters(rpt report.Report, topologies []APITopologyDesc) []APITopologyDesc { namespaces := map[string]struct{}{} - for _, t := range []report.Topology{rpt.Pod, rpt.Service} { + for _, t := range []report.Topology{rpt.Pod, rpt.Service, rpt.Deployment, rpt.ReplicaSet} { for _, n := range t.Nodes { if state, ok := n.Latest.Lookup(kubernetes.PodState); ok && state == kubernetes.StateDeleted { continue @@ -152,7 +166,7 @@ func updateFilters(rpt report.Report, topologies []APITopologyDesc) []APITopolog } sort.Strings(ns) for i, t := range topologies { - if t.id == "pods" || t.id == "pods-by-service" { + if t.id == "pods" || t.id == "services" || t.id == "deployments" || t.id == "replica-sets" { topologies[i] = updateTopologyFilters(t, []APITopologyOptionGroup{kubernetesFilters(ns...)}) } } @@ -227,7 +241,6 @@ func (r *registry) add(ts ...APITopologyDesc) { if t.parent != "" { parent := r.items[t.parent] parent.SubTopologies = append(parent.SubTopologies, t) - sort.Sort(byName(parent.SubTopologies)) r.items[t.parent] = parent } diff --git a/probe/appclient/app_client_internal_test.go b/probe/appclient/app_client_internal_test.go index 575f0e670a..21f96e568e 100644 --- a/probe/appclient/app_client_internal_test.go +++ b/probe/appclient/app_client_internal_test.go @@ -74,6 +74,8 @@ func TestAppClientPublish(t *testing.T) { rpt.ContainerImage = report.MakeTopology() rpt.Pod = report.MakeTopology() rpt.Service = report.MakeTopology() + rpt.Deployment = report.MakeTopology() + rpt.ReplicaSet = report.MakeTopology() rpt.Host = report.MakeTopology() rpt.Overlay = report.MakeTopology() rpt.Endpoint.Controls = nil @@ -82,6 +84,8 @@ func TestAppClientPublish(t *testing.T) { rpt.ContainerImage.Controls = nil rpt.Pod.Controls = nil rpt.Service.Controls = nil + rpt.Deployment.Controls = nil + rpt.ReplicaSet.Controls = nil rpt.Host.Controls = nil rpt.Overlay.Controls = nil diff --git a/probe/kubernetes/client.go b/probe/kubernetes/client.go index 2b6129f0c0..343b46f77e 100644 --- a/probe/kubernetes/client.go +++ b/probe/kubernetes/client.go @@ -8,6 +8,7 @@ import ( log "github.com/Sirupsen/logrus" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/client/cache" "k8s.io/kubernetes/pkg/client/restclient" "k8s.io/kubernetes/pkg/client/unversioned" @@ -26,6 +27,8 @@ type Client interface { Stop() WalkPods(f func(Pod) error) error WalkServices(f func(Service) error) error + WalkDeployments(f func(Deployment) error) error + WalkReplicaSets(f func(ReplicaSet) error) error WalkNodes(f func(*api.Node) error) error WatchPods(f func(Event, Pod)) @@ -35,14 +38,18 @@ type Client interface { } type client struct { - quit chan struct{} - client *unversioned.Client - podReflector *cache.Reflector - serviceReflector *cache.Reflector - nodeReflector *cache.Reflector - podStore *cache.StoreToPodLister - serviceStore *cache.StoreToServiceLister - nodeStore *cache.StoreToNodeLister + quit chan struct{} + client *unversioned.Client + podReflector *cache.Reflector + serviceReflector *cache.Reflector + deploymentReflector *cache.Reflector + replicaSetReflector *cache.Reflector + nodeReflector *cache.Reflector + podStore *cache.StoreToPodLister + serviceStore *cache.StoreToServiceLister + deploymentStore *cache.StoreToDeploymentLister + replicaSetStore *cache.StoreToReplicaSetLister + nodeStore *cache.StoreToNodeLister podWatchesMutex sync.Mutex podWatches []func(Event, Pod) @@ -80,6 +87,11 @@ func NewClient(addr string, resyncPeriod time.Duration) (Client, error) { return nil, err } + ec, err := unversioned.NewExtensions(config) + if err != nil { + return nil, err + } + result := &client{ quit: make(chan struct{}), client: c, @@ -95,6 +107,16 @@ func NewClient(addr string, resyncPeriod time.Duration) (Client, error) { result.serviceStore = &cache.StoreToServiceLister{Store: serviceStore} result.serviceReflector = cache.NewReflector(serviceListWatch, &api.Service{}, serviceStore, resyncPeriod) + deploymentListWatch := cache.NewListWatchFromClient(ec, "deployments", api.NamespaceAll, fields.Everything()) + deploymentStore := cache.NewStore(cache.MetaNamespaceKeyFunc) + result.deploymentStore = &cache.StoreToDeploymentLister{Store: deploymentStore} + result.deploymentReflector = cache.NewReflector(deploymentListWatch, &extensions.Deployment{}, deploymentStore, resyncPeriod) + + replicaSetListWatch := cache.NewListWatchFromClient(ec, "replicasets", api.NamespaceAll, fields.Everything()) + replicaSetStore := cache.NewStore(cache.MetaNamespaceKeyFunc) + result.replicaSetStore = &cache.StoreToReplicaSetLister{Store: replicaSetStore} + result.replicaSetReflector = cache.NewReflector(replicaSetListWatch, &extensions.ReplicaSet{}, replicaSetStore, resyncPeriod) + nodeListWatch := cache.NewListWatchFromClient(c, "nodes", api.NamespaceAll, fields.Everything()) nodeStore := cache.NewStore(cache.MetaNamespaceKeyFunc) result.nodeStore = &cache.StoreToNodeLister{Store: nodeStore} @@ -102,6 +124,8 @@ func NewClient(addr string, resyncPeriod time.Duration) (Client, error) { runReflectorUntil(result.podReflector, resyncPeriod, result.quit) runReflectorUntil(result.serviceReflector, resyncPeriod, result.quit) + runReflectorUntil(result.deploymentReflector, resyncPeriod, result.quit) + runReflectorUntil(result.replicaSetReflector, resyncPeriod, result.quit) runReflectorUntil(result.nodeReflector, resyncPeriod, result.quit) return result, nil } @@ -146,6 +170,32 @@ func (c *client) WalkServices(f func(Service) error) error { return nil } +func (c *client) WalkDeployments(f func(Deployment) error) error { + list, err := c.deploymentStore.List() + if err != nil { + return err + } + for i := range list { + if err := f(NewDeployment(&(list[i]))); err != nil { + return err + } + } + return nil +} + +func (c *client) WalkReplicaSets(f func(ReplicaSet) error) error { + list, err := c.replicaSetStore.List() + if err != nil { + return err + } + for i := range list { + if err := f(NewReplicaSet(&(list[i]))); err != nil { + return err + } + } + return nil +} + func (c *client) WalkNodes(f func(*api.Node) error) error { list, err := c.nodeStore.List() if err != nil { diff --git a/probe/kubernetes/deployment.go b/probe/kubernetes/deployment.go new file mode 100644 index 0000000000..ec2d48b241 --- /dev/null +++ b/probe/kubernetes/deployment.go @@ -0,0 +1,71 @@ +package kubernetes + +import ( + "fmt" + + "github.com/weaveworks/scope/report" + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/labels" +) + +// These constants are keys used in node metadata +const ( + DeploymentID = "kubernetes_deployment_id" + DeploymentName = "kubernetes_deployment_name" + DeploymentCreated = "kubernetes_deployment_created" + DeploymentObservedGeneration = "kubernetes_deployment_observed_generation" + DeploymentDesiredReplicas = "kubernetes_deployment_desired_replicas" + DeploymentReplicas = "kubernetes_deployment_replicas" + DeploymentUpdatedReplicas = "kubernetes_deployment_updated_replicas" + DeploymentAvailableReplicas = "kubernetes_deployment_available_replicas" + DeploymentUnavailableReplicas = "kubernetes_deployment_unavailable_replicas" + DeploymentLabelPrefix = "kubernetes_deployment_labels_" + DeploymentStrategy = "kubernetes_deployment_strategy" +) + +// Deployment represents a Kubernetes deployment +type Deployment interface { + Meta + Selector() labels.Selector + GetNode(probeID string) report.Node +} + +type deployment struct { + *extensions.Deployment + Meta + Node *api.Node +} + +// NewDeployment creates a new Deployment +func NewDeployment(d *extensions.Deployment) Deployment { + return &deployment{Deployment: d, Meta: meta{d.ObjectMeta}} +} + +func (d *deployment) Selector() labels.Selector { + selector, err := unversioned.LabelSelectorAsSelector(d.Spec.Selector) + if err != nil { + panic(err) + } + return selector +} + +func (d *deployment) GetNode(probeID string) report.Node { + n := report.MakeNodeWith(report.MakeDeploymentNodeID(d.UID()), map[string]string{ + DeploymentID: d.ID(), + DeploymentName: d.Name(), + Namespace: d.Namespace(), + DeploymentCreated: d.Created(), + DeploymentObservedGeneration: fmt.Sprint(d.Status.ObservedGeneration), + DeploymentDesiredReplicas: fmt.Sprint(d.Spec.Replicas), + DeploymentReplicas: fmt.Sprint(d.Status.Replicas), + DeploymentUpdatedReplicas: fmt.Sprint(d.Status.UpdatedReplicas), + DeploymentAvailableReplicas: fmt.Sprint(d.Status.AvailableReplicas), + DeploymentUnavailableReplicas: fmt.Sprint(d.Status.UnavailableReplicas), + DeploymentStrategy: string(d.Spec.Strategy.Type), + report.ControlProbeID: probeID, + }) + n = n.AddTable(DeploymentLabelPrefix, d.ObjectMeta.Labels) + return n +} diff --git a/probe/kubernetes/meta.go b/probe/kubernetes/meta.go new file mode 100644 index 0000000000..c50d574e46 --- /dev/null +++ b/probe/kubernetes/meta.go @@ -0,0 +1,46 @@ +package kubernetes + +import ( + "time" + + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/labels" +) + +// Meta represents a metadata information about a Kubernetes object +type Meta interface { + UID() string + ID() string + Name() string + Namespace() string + Created() string + Labels() labels.Labels +} + +type meta struct { + ObjectMeta api.ObjectMeta +} + +func (m meta) UID() string { + return string(m.ObjectMeta.UID) +} + +func (m meta) ID() string { + return m.ObjectMeta.Namespace + "/" + m.ObjectMeta.Name +} + +func (m meta) Name() string { + return m.ObjectMeta.Name +} + +func (m meta) Namespace() string { + return m.ObjectMeta.Namespace +} + +func (m meta) Created() string { + return m.ObjectMeta.CreationTimestamp.Format(time.RFC822) +} + +func (m meta) Labels() labels.Labels { + return labels.Set(m.ObjectMeta.Labels) +} diff --git a/probe/kubernetes/pod.go b/probe/kubernetes/pod.go index 42a9c3df18..9182899dad 100644 --- a/probe/kubernetes/pod.go +++ b/probe/kubernetes/pod.go @@ -13,7 +13,6 @@ const ( PodState = "kubernetes_pod_state" PodLabelPrefix = "kubernetes_pod_labels_" PodIP = "kubernetes_pod_ip" - ServiceIDs = "kubernetes_service_ids" StateDeleted = "deleted" ) @@ -21,7 +20,7 @@ const ( // Pod represents a Kubernetes pod type Pod interface { Meta - AddServiceID(id string) + AddParent(topology, id string) NodeName() string GetNode(probeID string) report.Node } @@ -29,13 +28,13 @@ type Pod interface { type pod struct { *api.Pod Meta - serviceIDs report.StringSet - Node *api.Node + parents report.Sets + Node *api.Node } // NewPod creates a new Pod func NewPod(p *api.Pod) Pod { - return &pod{Pod: p, Meta: meta{p.ObjectMeta}, serviceIDs: report.MakeStringSet()} + return &pod{Pod: p, Meta: meta{p.ObjectMeta}, parents: report.MakeSets()} } func (p *pod) UID() string { @@ -46,8 +45,8 @@ func (p *pod) UID() string { return p.Meta.UID() } -func (p *pod) AddServiceID(id string) { - p.serviceIDs = p.serviceIDs.Add(id) +func (p *pod) AddParent(topology, id string) { + p.parents = p.parents.Add(topology, report.MakeStringSet(id)) } func (p *pod) State() string { @@ -67,12 +66,7 @@ func (p *pod) GetNode(probeID string) report.Node { PodState: p.State(), PodIP: p.Status.PodIP, report.ControlProbeID: probeID, - }).WithSets(report.EmptySets.Add(ServiceIDs, p.serviceIDs)) - for _, serviceID := range p.serviceIDs { - n = n.WithParents(report.EmptySets. - Add(report.Service, report.MakeStringSet(report.MakeServiceNodeID(serviceID))), - ) - } + }).WithParents(p.parents) n = n.AddTable(PodLabelPrefix, p.ObjectMeta.Labels) n = n.WithControls(GetLogs, DeletePod) return n diff --git a/probe/kubernetes/replica_set.go b/probe/kubernetes/replica_set.go new file mode 100644 index 0000000000..3b5696b2e3 --- /dev/null +++ b/probe/kubernetes/replica_set.go @@ -0,0 +1,65 @@ +package kubernetes + +import ( + "fmt" + + "github.com/weaveworks/scope/report" + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/labels" +) + +// These constants are keys used in node metadata +const ( + ReplicaSetID = "kubernetes_replica_set_id" + ReplicaSetName = "kubernetes_replica_set_name" + ReplicaSetCreated = "kubernetes_replica_set_created" + ReplicaSetObservedGeneration = "kubernetes_replica_set_observed_generation" + ReplicaSetReplicas = "kubernetes_replica_set_replicas" + ReplicaSetDesiredReplicas = "kubernetes_replica_set_desired_replicas" + ReplicaSetFullyLabeledReplicas = "kubernetes_replica_set_fully_labeled_replicas" + ReplicaSetLabelPrefix = "kubernetes_replica_set_labels_" +) + +// ReplicaSet represents a Kubernetes replica_set +type ReplicaSet interface { + Meta + Selector() labels.Selector + GetNode(probeID string) report.Node +} + +type replicaSet struct { + *extensions.ReplicaSet + Meta + Node *api.Node +} + +// NewReplicaSet creates a new ReplicaSet +func NewReplicaSet(d *extensions.ReplicaSet) ReplicaSet { + return &replicaSet{ReplicaSet: d, Meta: meta{d.ObjectMeta}} +} + +func (d *replicaSet) Selector() labels.Selector { + selector, err := unversioned.LabelSelectorAsSelector(d.Spec.Selector) + if err != nil { + panic(err) + } + return selector +} + +func (d *replicaSet) GetNode(probeID string) report.Node { + n := report.MakeNodeWith(report.MakeReplicaSetNodeID(d.UID()), map[string]string{ + ReplicaSetID: d.ID(), + ReplicaSetName: d.Name(), + Namespace: d.Namespace(), + ReplicaSetCreated: d.Created(), + ReplicaSetObservedGeneration: fmt.Sprint(d.Status.ObservedGeneration), + ReplicaSetReplicas: fmt.Sprint(d.Status.Replicas), + ReplicaSetDesiredReplicas: fmt.Sprint(d.Spec.Replicas), + ReplicaSetFullyLabeledReplicas: fmt.Sprint(d.Status.FullyLabeledReplicas), + report.ControlProbeID: probeID, + }) + n = n.AddTable(ReplicaSetLabelPrefix, d.ObjectMeta.Labels) + return n +} diff --git a/probe/kubernetes/reporter.go b/probe/kubernetes/reporter.go index 2fabdfdcbf..df252a7b5d 100644 --- a/probe/kubernetes/reporter.go +++ b/probe/kubernetes/reporter.go @@ -35,6 +35,25 @@ var ( report.Pod: {ID: report.Pod, Label: "# Pods", From: report.FromCounters, Datatype: "number", Priority: 6}, } + DeploymentMetadataTemplates = report.MetadataTemplates{ + DeploymentID: {ID: DeploymentID, Label: "ID", From: report.FromLatest, Priority: 1}, + Namespace: {ID: Namespace, Label: "Namespace", From: report.FromLatest, Priority: 2}, + DeploymentCreated: {ID: DeploymentCreated, Label: "Created", From: report.FromLatest, Priority: 3}, + DeploymentObservedGeneration: {ID: DeploymentObservedGeneration, Label: "Observed Gen.", From: report.FromLatest, Priority: 4}, + DeploymentDesiredReplicas: {ID: DeploymentDesiredReplicas, Label: "Desired Replicas", From: report.FromLatest, Datatype: "number", Priority: 5}, + report.Pod: {ID: report.Pod, Label: "# Pods", From: report.FromCounters, Datatype: "number", Priority: 6}, + DeploymentStrategy: {ID: DeploymentStrategy, Label: "Strategy", From: report.FromLatest, Priority: 7}, + } + + ReplicaSetMetadataTemplates = report.MetadataTemplates{ + ReplicaSetID: {ID: ReplicaSetID, Label: "ID", From: report.FromLatest, Priority: 1}, + Namespace: {ID: Namespace, Label: "Namespace", From: report.FromLatest, Priority: 2}, + ReplicaSetCreated: {ID: ReplicaSetCreated, Label: "Created", From: report.FromLatest, Priority: 3}, + ReplicaSetObservedGeneration: {ID: ReplicaSetObservedGeneration, Label: "Observed Gen.", From: report.FromLatest, Priority: 4}, + ReplicaSetDesiredReplicas: {ID: ReplicaSetDesiredReplicas, Label: "Desired Replicas", From: report.FromLatest, Datatype: "number", Priority: 5}, + report.Pod: {ID: report.Pod, Label: "# Pods", From: report.FromCounters, Datatype: "number", Priority: 6}, + } + PodTableTemplates = report.TableTemplates{ PodLabelPrefix: {ID: PodLabelPrefix, Label: "Kubernetes Labels", Prefix: PodLabelPrefix}, } @@ -42,6 +61,14 @@ var ( ServiceTableTemplates = report.TableTemplates{ ServiceLabelPrefix: {ID: ServiceLabelPrefix, Label: "Kubernetes Labels", Prefix: ServiceLabelPrefix}, } + + DeploymentTableTemplates = report.TableTemplates{ + DeploymentLabelPrefix: {ID: DeploymentLabelPrefix, Label: "Kubernetes Labels", Prefix: DeploymentLabelPrefix}, + } + + ReplicaSetTableTemplates = report.TableTemplates{ + ReplicaSetLabelPrefix: {ID: ReplicaSetLabelPrefix, Label: "Kubernetes Labels", Prefix: ReplicaSetLabelPrefix}, + } ) // Reporter generate Reports containing Container and ContainerImage topologies @@ -142,12 +169,22 @@ func (r *Reporter) Report() (report.Report, error) { if err != nil { return result, err } - podTopology, err := r.podTopology(services) + deploymentTopology, deployments, err := r.deploymentTopology(r.probeID) + if err != nil { + return result, err + } + replicaSetTopology, replicaSets, err := r.replicaSetTopology(r.probeID) + if err != nil { + return result, err + } + podTopology, err := r.podTopology(services, deployments, replicaSets) if err != nil { return result, err } - result.Service = result.Service.Merge(serviceTopology) result.Pod = result.Pod.Merge(podTopology) + result.Service = result.Service.Merge(serviceTopology) + result.Deployment = result.Deployment.Merge(deploymentTopology) + result.ReplicaSet = result.ReplicaSet.Merge(replicaSetTopology) return result, nil } @@ -166,6 +203,36 @@ func (r *Reporter) serviceTopology() (report.Topology, []Service, error) { return result, services, err } +func (r *Reporter) deploymentTopology(probeID string) (report.Topology, []Deployment, error) { + var ( + result = report.MakeTopology(). + WithMetadataTemplates(DeploymentMetadataTemplates). + WithTableTemplates(DeploymentTableTemplates) + deployments = []Deployment{} + ) + err := r.client.WalkDeployments(func(d Deployment) error { + result = result.AddNode(d.GetNode(probeID)) + deployments = append(deployments, d) + return nil + }) + return result, deployments, err +} + +func (r *Reporter) replicaSetTopology(probeID string) (report.Topology, []ReplicaSet, error) { + var ( + result = report.MakeTopology(). + WithMetadataTemplates(ReplicaSetMetadataTemplates). + WithTableTemplates(ReplicaSetTableTemplates) + replicaSets = []ReplicaSet{} + ) + err := r.client.WalkReplicaSets(func(r ReplicaSet) error { + result = result.AddNode(r.GetNode(probeID)) + replicaSets = append(replicaSets, r) + return nil + }) + return result, replicaSets, err +} + // GetNodeName return the k8s node name for the current machine. // It is exported for testing. var GetNodeName = func(r *Reporter) (string, error) { @@ -187,16 +254,16 @@ var GetNodeName = func(r *Reporter) (string, error) { return nodeName, err } -func (r *Reporter) podTopology(services []Service) (report.Topology, error) { +func (r *Reporter) podTopology(services []Service, deployments []Deployment, replicaSets []ReplicaSet) (report.Topology, error) { var ( pods = report.MakeTopology(). WithMetadataTemplates(PodMetadataTemplates). WithTableTemplates(PodTableTemplates) selectors = []func(Pod){} - match = func(selector labels.Selector, f func(Pod, string), id string) func(Pod) { + match = func(selector labels.Selector, topology, id string) func(Pod) { return func(p Pod) { if selector.Matches(p.Labels()) { - f(p, id) + p.AddParent(topology, id) } } } @@ -214,7 +281,25 @@ func (r *Reporter) podTopology(services []Service) (report.Topology, error) { Rank: 1, }) for _, service := range services { - selectors = append(selectors, match(service.Selector(), Pod.AddServiceID, service.UID())) + selectors = append(selectors, match( + service.Selector(), + report.Service, + report.MakeServiceNodeID(service.UID()), + )) + } + for _, deployment := range deployments { + selectors = append(selectors, match( + deployment.Selector(), + report.Deployment, + report.MakeDeploymentNodeID(deployment.UID()), + )) + } + for _, replicaSet := range replicaSets { + selectors = append(selectors, match( + replicaSet.Selector(), + report.ReplicaSet, + report.MakeReplicaSetNodeID(replicaSet.UID()), + )) } thisNodeName, err := GetNodeName(r) diff --git a/probe/kubernetes/reporter_test.go b/probe/kubernetes/reporter_test.go index 985b9304a8..c8c19d7c0a 100644 --- a/probe/kubernetes/reporter_test.go +++ b/probe/kubernetes/reporter_test.go @@ -131,6 +131,12 @@ func (c *mockClient) WalkServices(f func(kubernetes.Service) error) error { } return nil } +func (c *mockClient) WalkDeployments(f func(kubernetes.Deployment) error) error { + return nil +} +func (c *mockClient) WalkReplicaSets(f func(kubernetes.ReplicaSet) error) error { + return nil +} func (*mockClient) WalkNodes(f func(*api.Node) error) error { return nil } @@ -176,23 +182,18 @@ func TestReporter(t *testing.T) { id string parentService string latest map[string]string - sets map[string]report.StringSet }{ {pod1ID, serviceID, map[string]string{ kubernetes.PodID: "ping/pong-a", kubernetes.PodName: "pong-a", kubernetes.Namespace: "ping", kubernetes.PodCreated: pod1.Created(), - }, map[string]report.StringSet{ - kubernetes.ServiceIDs: report.MakeStringSet(serviceUID), }}, {pod2ID, serviceID, map[string]string{ kubernetes.PodID: "ping/pong-b", kubernetes.PodName: "pong-b", kubernetes.Namespace: "ping", kubernetes.PodCreated: pod1.Created(), - }, map[string]report.StringSet{ - kubernetes.ServiceIDs: report.MakeStringSet(serviceUID), }}, } { node, ok := rpt.Pod.Nodes[pod.id] @@ -209,12 +210,6 @@ func TestReporter(t *testing.T) { t.Errorf("Expected pod %s latest %q: %q, got %q", pod.id, k, want, have) } } - - for k, want := range pod.sets { - if have, ok := node.Sets.Lookup(k); !ok || !reflect.DeepEqual(want, have) { - t.Errorf("Expected pod %s sets %q: %q, got %q", pod.id, k, want, have) - } - } } // Reporter should have added a service diff --git a/probe/probe_internal_test.go b/probe/probe_internal_test.go index 81ab7e0487..5b4aac0f4a 100644 --- a/probe/probe_internal_test.go +++ b/probe/probe_internal_test.go @@ -87,6 +87,8 @@ func TestProbe(t *testing.T) { want.ContainerImage.Controls = nil want.Pod.Controls = nil want.Service.Controls = nil + want.Deployment.Controls = nil + want.ReplicaSet.Controls = nil want.Host.Controls = nil want.Overlay.Controls = nil want.Endpoint.AddNode(node) diff --git a/render/detailed/node_test.go b/render/detailed/node_test.go index c96fd671de..62acaec754 100644 --- a/render/detailed/node_test.go +++ b/render/detailed/node_test.go @@ -361,6 +361,11 @@ func TestMakeDetailedPodNode(t *testing.T) { Label: fixture.ServerHostName, TopologyID: "hosts", }, + { + ID: fixture.ServiceNodeID, + Label: fixture.ServiceName, + TopologyID: "pods-by-service", + }, }, Connections: []detailed.ConnectionsSummary{ { diff --git a/render/detailed/summary.go b/render/detailed/summary.go index c00962de2d..099e2a1771 100644 --- a/render/detailed/summary.go +++ b/render/detailed/summary.go @@ -78,6 +78,8 @@ func MakeNodeSummary(r report.Report, n report.Node) (NodeSummary, bool) { report.ContainerImage: containerImageNodeSummary, report.Pod: podNodeSummary, report.Service: serviceNodeSummary, + report.Deployment: deploymentNodeSummary, + report.ReplicaSet: replicaSetNodeSummary, report.Host: hostNodeSummary, } if renderer, ok := renderers[n.Topology]; ok { @@ -270,6 +272,38 @@ func serviceNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) { return base, true } +func deploymentNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) { + base.Label, _ = n.Latest.Lookup(kubernetes.DeploymentName) + base.Rank, _ = n.Latest.Lookup(kubernetes.DeploymentID) + base.Stack = true + + if p, ok := n.Counters.Lookup(report.Pod); ok { + if p == 1 { + base.LabelMinor = fmt.Sprintf("%d pod", p) + } else { + base.LabelMinor = fmt.Sprintf("%d pods", p) + } + } + + return base, true +} + +func replicaSetNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) { + base.Label, _ = n.Latest.Lookup(kubernetes.ReplicaSetName) + base.Rank, _ = n.Latest.Lookup(kubernetes.ReplicaSetID) + base.Stack = true + + if p, ok := n.Counters.Lookup(report.Pod); ok { + if p == 1 { + base.LabelMinor = fmt.Sprintf("%d pod", p) + } else { + base.LabelMinor = fmt.Sprintf("%d pods", p) + } + } + + return base, true +} + func hostNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) { var ( hostname, _ = n.Latest.Lookup(host.HostName) diff --git a/render/pod.go b/render/pod.go index f8b048961b..a057ff3d7b 100644 --- a/render/pod.go +++ b/render/pod.go @@ -50,6 +50,34 @@ var PodServiceRenderer = ConditionalRenderer(renderKubernetesTopologies, )), ) +// DeploymentRenderer is a Renderer which produces a renderable kubernetes deployments +// graph by merging the pods graph and the deployments topology. +var DeploymentRenderer = ApplyDecorators( + FilterEmpty(report.Pod, + MakeReduce( + MakeMap( + MapPod2Deployment, + PodRenderer, + ), + SelectDeployment, + ), + ), +) + +// ReplicaSetRenderer is a Renderer which produces a renderable kubernetes replica sets +// graph by merging the pods graph and the replica sets topology. +var ReplicaSetRenderer = ApplyDecorators( + FilterEmpty(report.Pod, + MakeReduce( + MakeMap( + MapPod2ReplicaSet, + PodRenderer, + ), + SelectReplicaSet, + ), + ), +) + // MapContainer2Pod maps container Nodes to pod // Nodes. // @@ -97,11 +125,13 @@ func MapContainer2Pod(n report.Node, _ report.Networks) report.Nodes { // The various ways of grouping pods var ( - MapPod2Service = MapPod2Grouping(report.Service, kubernetes.ServiceIDs, report.MakeServiceNodeID) + MapPod2Service = MapPod2Parent(report.Service) + MapPod2Deployment = MapPod2Parent(report.Deployment) + MapPod2ReplicaSet = MapPod2Parent(report.ReplicaSet) ) -// 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 { +// MapPod2Parent maps pod Nodes to some kubernetes grouping. +func MapPod2Parent(topology 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 { @@ -111,14 +141,13 @@ func MapPod2Grouping(topology, setKey string, idMaker func(uid string) string) f // 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) + groupIDs, ok := pod.Parents.Lookup(topology) if !ok { return report.Nodes{} } result := report.Nodes{} - for _, groupID := range groupIDs { - id := idMaker(groupID) + for _, id := range groupIDs { node := NewDerivedNode(id, pod).WithTopology(topology) node.Counters = node.Counters.Add(pod.Topology, 1) result[id] = node diff --git a/render/selectors.go b/render/selectors.go index b1bba8213a..fa53447493 100644 --- a/render/selectors.go +++ b/render/selectors.go @@ -29,4 +29,6 @@ var ( SelectHost = TopologySelector(report.Host) SelectPod = TopologySelector(report.Pod) SelectService = TopologySelector(report.Service) + SelectDeployment = TopologySelector(report.Deployment) + SelectReplicaSet = TopologySelector(report.ReplicaSet) ) diff --git a/report/id.go b/report/id.go index ae4ebfc136..8a364e6838 100644 --- a/report/id.go +++ b/report/id.go @@ -111,6 +111,12 @@ var ( // MakeServiceNodeID produces a service node ID from its composite parts. MakeServiceNodeID = singleComponentID("service") + + // MakeDeploymentNodeID produces a deployment node ID from its composite parts. + MakeDeploymentNodeID = singleComponentID("deployment") + + // MakeReplicaSetNodeID produces a replica set node ID from its composite parts. + MakeReplicaSetNodeID = singleComponentID("replica_set") ) // singleComponentID makes a diff --git a/report/report.go b/report/report.go index 493dcb55e1..610d9d13b2 100644 --- a/report/report.go +++ b/report/report.go @@ -16,6 +16,8 @@ const ( Container = "container" Pod = "pod" Service = "service" + Deployment = "deployment" + ReplicaSet = "replica_set" ContainerImage = "container_image" Host = "host" Overlay = "overlay" @@ -58,6 +60,16 @@ type Report struct { // present. Service Topology + // Deployment nodes represent all Kubernetes deployments running on hosts running probes. + // Metadata includes things like deployment id, name etc. Edges are not + // present. + Deployment Topology + + // ReplicaSet nodes represent all Kubernetes replicasets running on hosts running probes. + // Metadata includes things like replicaset id, name etc. Edges are not + // present. + ReplicaSet Topology + // ContainerImages nodes represent all Docker containers images on // hosts running probes. Metadata includes things like image id, name etc. // Edges are not present. @@ -126,6 +138,14 @@ func MakeReport() Report { WithShape(Heptagon). WithLabel("service", "services"), + Deployment: MakeTopology(). + WithShape(Heptagon). + WithLabel("deployment", "deployments"), + + ReplicaSet: MakeTopology(). + WithShape(Heptagon). + WithLabel("replica set", "replica sets"), + Overlay: MakeTopology(), Sampling: Sampling{}, @@ -145,6 +165,8 @@ func (r Report) Copy() Report { Host: r.Host.Copy(), Pod: r.Pod.Copy(), Service: r.Service.Copy(), + Deployment: r.Deployment.Copy(), + ReplicaSet: r.ReplicaSet.Copy(), Overlay: r.Overlay.Copy(), Sampling: r.Sampling, Window: r.Window, @@ -164,6 +186,8 @@ func (r Report) Merge(other Report) Report { cp.Host = r.Host.Merge(other.Host) cp.Pod = r.Pod.Merge(other.Pod) cp.Service = r.Service.Merge(other.Service) + cp.Deployment = r.Deployment.Merge(other.Deployment) + cp.ReplicaSet = r.ReplicaSet.Merge(other.ReplicaSet) cp.Overlay = r.Overlay.Merge(other.Overlay) cp.Sampling = r.Sampling.Merge(other.Sampling) cp.Window += other.Window @@ -180,6 +204,8 @@ func (r Report) Topologies() []Topology { r.ContainerImage, r.Pod, r.Service, + r.Deployment, + r.ReplicaSet, r.Host, r.Overlay, } @@ -194,6 +220,8 @@ func (r Report) Topology(name string) (Topology, bool) { ContainerImage: r.ContainerImage, Pod: r.Pod, Service: r.Service, + Deployment: r.Deployment, + ReplicaSet: r.ReplicaSet, Host: r.Host, Overlay: r.Overlay, }[name] diff --git a/test/fixture/report_fixture.go b/test/fixture/report_fixture.go index 91ef83886f..4c54d7033b 100644 --- a/test/fixture/report_fixture.go +++ b/test/fixture/report_fixture.go @@ -96,6 +96,7 @@ var ( ServerPodUID = "i9h8g7f6e" ClientPodNodeID = report.MakePodNodeID(ClientPodUID) ServerPodNodeID = report.MakePodNodeID(ServerPodUID) + ServiceName = "pongservice" ServiceID = "ping/pongservice" ServiceUID = "service1234" ServiceNodeID = report.MakeServiceNodeID(ServiceUID) @@ -373,11 +374,9 @@ var ( kubernetes.Namespace: KubernetesNamespace, report.HostNodeID: ClientHostNodeID, }). - WithSets(report.EmptySets. - Add(kubernetes.ServiceIDs, report.MakeStringSet(ServiceUID))). WithTopology(report.Pod).WithParents(report.EmptySets. Add("host", report.MakeStringSet(ClientHostNodeID)). - Add("service", report.MakeStringSet(ServiceID)), + Add("service", report.MakeStringSet(ServiceNodeID)), ), ServerPodNodeID: report.MakeNodeWith( ServerPodNodeID, map[string]string{ @@ -387,11 +386,9 @@ var ( kubernetes.PodState: "running", report.HostNodeID: ServerHostNodeID, }). - WithSets(report.EmptySets. - Add(kubernetes.ServiceIDs, report.MakeStringSet(ServiceUID))). WithTopology(report.Pod).WithParents(report.EmptySets. Add("host", report.MakeStringSet(ServerHostNodeID)). - Add("service", report.MakeStringSet(ServiceID)), + Add("service", report.MakeStringSet(ServiceNodeID)), ), }, MetadataTemplates: kubernetes.PodMetadataTemplates, From 541699d193ca882ad8c2787c2ae7029e3f2a5410 Mon Sep 17 00:00:00 2001 From: Paul Bellamy Date: Fri, 6 May 2016 14:12:27 +0100 Subject: [PATCH 3/4] Review Feedback --- app/api_topologies.go | 2 +- probe/kubernetes/client.go | 103 ++++++++-------- probe/kubernetes/deployment.go | 40 +++---- probe/kubernetes/meta.go | 29 ++++- probe/kubernetes/pod.go | 30 ++--- probe/kubernetes/replica_set.go | 54 +++++---- probe/kubernetes/replication_controller.go | 46 ++++++++ probe/kubernetes/reporter.go | 130 +++++++++++---------- probe/kubernetes/reporter_test.go | 24 ++-- probe/kubernetes/service.go | 23 +--- render/detailed/node.go | 31 +++-- render/detailed/node_test.go | 6 +- render/detailed/parents.go | 4 +- render/detailed/summary.go | 16 +-- render/pod.go | 34 +++--- test/fixture/report_fixture.go | 16 +-- 16 files changed, 315 insertions(+), 273 deletions(-) create mode 100644 probe/kubernetes/replication_controller.go diff --git a/app/api_topologies.go b/app/api_topologies.go index 3fb0be82bf..9f690236c9 100644 --- a/app/api_topologies.go +++ b/app/api_topologies.go @@ -152,7 +152,7 @@ func updateFilters(rpt report.Report, topologies []APITopologyDesc) []APITopolog namespaces := map[string]struct{}{} for _, t := range []report.Topology{rpt.Pod, rpt.Service, rpt.Deployment, rpt.ReplicaSet} { for _, n := range t.Nodes { - if state, ok := n.Latest.Lookup(kubernetes.PodState); ok && state == kubernetes.StateDeleted { + if state, ok := n.Latest.Lookup(kubernetes.State); ok && state == kubernetes.StateDeleted { continue } if namespace, ok := n.Latest.Lookup(kubernetes.Namespace); ok { diff --git a/probe/kubernetes/client.go b/probe/kubernetes/client.go index 343b46f77e..76ec6142c3 100644 --- a/probe/kubernetes/client.go +++ b/probe/kubernetes/client.go @@ -17,11 +17,6 @@ import ( "k8s.io/kubernetes/pkg/util/wait" ) -// These constants are keys used in node metadata -const ( - Namespace = "kubernetes_namespace" -) - // Client keeps track of running kubernetes pods and services type Client interface { Stop() @@ -38,18 +33,15 @@ type Client interface { } type client struct { - quit chan struct{} - client *unversioned.Client - podReflector *cache.Reflector - serviceReflector *cache.Reflector - deploymentReflector *cache.Reflector - replicaSetReflector *cache.Reflector - nodeReflector *cache.Reflector - podStore *cache.StoreToPodLister - serviceStore *cache.StoreToServiceLister - deploymentStore *cache.StoreToDeploymentLister - replicaSetStore *cache.StoreToReplicaSetLister - nodeStore *cache.StoreToNodeLister + quit chan struct{} + resyncPeriod time.Duration + client *unversioned.Client + podStore *cache.StoreToPodLister + serviceStore *cache.StoreToServiceLister + deploymentStore *cache.StoreToDeploymentLister + replicaSetStore *cache.StoreToReplicaSetLister + replicationControllerStore *cache.StoreToReplicationControllerLister + nodeStore *cache.StoreToNodeLister podWatchesMutex sync.Mutex podWatches []func(Event, Pod) @@ -93,43 +85,27 @@ func NewClient(addr string, resyncPeriod time.Duration) (Client, error) { } result := &client{ - quit: make(chan struct{}), - client: c, - } - - podListWatch := cache.NewListWatchFromClient(c, "pods", api.NamespaceAll, fields.Everything()) - podStore := NewEventStore(result.triggerPodWatches, cache.MetaNamespaceKeyFunc) - result.podStore = &cache.StoreToPodLister{Store: podStore} - result.podReflector = cache.NewReflector(podListWatch, &api.Pod{}, podStore, resyncPeriod) - - serviceListWatch := cache.NewListWatchFromClient(c, "services", api.NamespaceAll, fields.Everything()) - serviceStore := cache.NewStore(cache.MetaNamespaceKeyFunc) - result.serviceStore = &cache.StoreToServiceLister{Store: serviceStore} - result.serviceReflector = cache.NewReflector(serviceListWatch, &api.Service{}, serviceStore, resyncPeriod) - - deploymentListWatch := cache.NewListWatchFromClient(ec, "deployments", api.NamespaceAll, fields.Everything()) - deploymentStore := cache.NewStore(cache.MetaNamespaceKeyFunc) - result.deploymentStore = &cache.StoreToDeploymentLister{Store: deploymentStore} - result.deploymentReflector = cache.NewReflector(deploymentListWatch, &extensions.Deployment{}, deploymentStore, resyncPeriod) - - replicaSetListWatch := cache.NewListWatchFromClient(ec, "replicasets", api.NamespaceAll, fields.Everything()) - replicaSetStore := cache.NewStore(cache.MetaNamespaceKeyFunc) - result.replicaSetStore = &cache.StoreToReplicaSetLister{Store: replicaSetStore} - result.replicaSetReflector = cache.NewReflector(replicaSetListWatch, &extensions.ReplicaSet{}, replicaSetStore, resyncPeriod) - - nodeListWatch := cache.NewListWatchFromClient(c, "nodes", api.NamespaceAll, fields.Everything()) - nodeStore := cache.NewStore(cache.MetaNamespaceKeyFunc) - result.nodeStore = &cache.StoreToNodeLister{Store: nodeStore} - result.nodeReflector = cache.NewReflector(nodeListWatch, &api.Node{}, nodeStore, resyncPeriod) - - runReflectorUntil(result.podReflector, resyncPeriod, result.quit) - runReflectorUntil(result.serviceReflector, resyncPeriod, result.quit) - runReflectorUntil(result.deploymentReflector, resyncPeriod, result.quit) - runReflectorUntil(result.replicaSetReflector, resyncPeriod, result.quit) - runReflectorUntil(result.nodeReflector, resyncPeriod, result.quit) + quit: make(chan struct{}), + resyncPeriod: resyncPeriod, + client: c, + } + + result.podStore = &cache.StoreToPodLister{Store: result.setupStore(c, "pods", &api.Pod{})} + result.serviceStore = &cache.StoreToServiceLister{Store: result.setupStore(c, "services", &api.Service{})} + result.deploymentStore = &cache.StoreToDeploymentLister{Store: result.setupStore(ec, "deployments", &extensions.Deployment{})} + result.replicaSetStore = &cache.StoreToReplicaSetLister{Store: result.setupStore(ec, "replicasets", &extensions.ReplicaSet{})} + result.replicationControllerStore = &cache.StoreToReplicationControllerLister{Store: result.setupStore(c, "replicationcontrollers", &api.ReplicationController{})} + result.nodeStore = &cache.StoreToNodeLister{Store: result.setupStore(c, "nodes", &api.Node{})} return result, nil } +func (c *client) setupStore(kclient cache.Getter, resource string, itemType interface{}) cache.Store { + lw := cache.NewListWatchFromClient(kclient, resource, api.NamespaceAll, fields.Everything()) + store := cache.NewStore(cache.MetaNamespaceKeyFunc) + runReflectorUntil(cache.NewReflector(lw, itemType, store, c.resyncPeriod), c.resyncPeriod, c.quit) + return store +} + func (c *client) WatchPods(f func(Event, Pod)) { c.podWatchesMutex.Lock() defer c.podWatchesMutex.Unlock() @@ -183,15 +159,30 @@ func (c *client) WalkDeployments(f func(Deployment) error) error { return nil } +// WalkReplicaSets calls f for each replica set (and replication controller) func (c *client) WalkReplicaSets(f func(ReplicaSet) error) error { - list, err := c.replicaSetStore.List() - if err != nil { - return err + { + list, err := c.replicaSetStore.List() + if err != nil { + return err + } + for i := range list { + if err := f(NewReplicaSet(&(list[i]))); err != nil { + return err + } + } } - for i := range list { - if err := f(NewReplicaSet(&(list[i]))); err != nil { + + { + list, err := c.replicationControllerStore.List() + if err != nil { return err } + for i := range list { + if err := f(NewReplicationController(&(list[i]))); err != nil { + return err + } + } } return nil } diff --git a/probe/kubernetes/deployment.go b/probe/kubernetes/deployment.go index ec2d48b241..513e3a3a8b 100644 --- a/probe/kubernetes/deployment.go +++ b/probe/kubernetes/deployment.go @@ -12,17 +12,10 @@ import ( // These constants are keys used in node metadata const ( - DeploymentID = "kubernetes_deployment_id" - DeploymentName = "kubernetes_deployment_name" - DeploymentCreated = "kubernetes_deployment_created" - DeploymentObservedGeneration = "kubernetes_deployment_observed_generation" - DeploymentDesiredReplicas = "kubernetes_deployment_desired_replicas" - DeploymentReplicas = "kubernetes_deployment_replicas" - DeploymentUpdatedReplicas = "kubernetes_deployment_updated_replicas" - DeploymentAvailableReplicas = "kubernetes_deployment_available_replicas" - DeploymentUnavailableReplicas = "kubernetes_deployment_unavailable_replicas" - DeploymentLabelPrefix = "kubernetes_deployment_labels_" - DeploymentStrategy = "kubernetes_deployment_strategy" + UpdatedReplicas = "kubernetes_updated_replicas" + AvailableReplicas = "kubernetes_available_replicas" + UnavailableReplicas = "kubernetes_unavailable_replicas" + Strategy = "kubernetes_strategy" ) // Deployment represents a Kubernetes deployment @@ -46,26 +39,21 @@ func NewDeployment(d *extensions.Deployment) Deployment { func (d *deployment) Selector() labels.Selector { selector, err := unversioned.LabelSelectorAsSelector(d.Spec.Selector) if err != nil { + // TODO(paulbellamy): Remove the panic! panic(err) } return selector } func (d *deployment) GetNode(probeID string) report.Node { - n := report.MakeNodeWith(report.MakeDeploymentNodeID(d.UID()), map[string]string{ - DeploymentID: d.ID(), - DeploymentName: d.Name(), - Namespace: d.Namespace(), - DeploymentCreated: d.Created(), - DeploymentObservedGeneration: fmt.Sprint(d.Status.ObservedGeneration), - DeploymentDesiredReplicas: fmt.Sprint(d.Spec.Replicas), - DeploymentReplicas: fmt.Sprint(d.Status.Replicas), - DeploymentUpdatedReplicas: fmt.Sprint(d.Status.UpdatedReplicas), - DeploymentAvailableReplicas: fmt.Sprint(d.Status.AvailableReplicas), - DeploymentUnavailableReplicas: fmt.Sprint(d.Status.UnavailableReplicas), - DeploymentStrategy: string(d.Spec.Strategy.Type), - report.ControlProbeID: probeID, + return d.MetaNode(report.MakeDeploymentNodeID(d.UID())).WithLatests(map[string]string{ + ObservedGeneration: fmt.Sprint(d.Status.ObservedGeneration), + DesiredReplicas: fmt.Sprint(d.Spec.Replicas), + Replicas: fmt.Sprint(d.Status.Replicas), + UpdatedReplicas: fmt.Sprint(d.Status.UpdatedReplicas), + AvailableReplicas: fmt.Sprint(d.Status.AvailableReplicas), + UnavailableReplicas: fmt.Sprint(d.Status.UnavailableReplicas), + Strategy: string(d.Spec.Strategy.Type), + report.ControlProbeID: probeID, }) - n = n.AddTable(DeploymentLabelPrefix, d.ObjectMeta.Labels) - return n } diff --git a/probe/kubernetes/meta.go b/probe/kubernetes/meta.go index c50d574e46..00958ea672 100644 --- a/probe/kubernetes/meta.go +++ b/probe/kubernetes/meta.go @@ -4,7 +4,17 @@ import ( "time" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/labels" + + "github.com/weaveworks/scope/report" +) + +// These constants are keys used in node metadata +const ( + ID = "kubernetes_id" + Name = "kubernetes_name" + Namespace = "kubernetes_namespace" + Created = "kubernetes_created" + LabelPrefix = "kubernetes_labels_" ) // Meta represents a metadata information about a Kubernetes object @@ -14,7 +24,8 @@ type Meta interface { Name() string Namespace() string Created() string - Labels() labels.Labels + Labels() map[string]string + MetaNode(id string) report.Node } type meta struct { @@ -41,6 +52,16 @@ func (m meta) Created() string { return m.ObjectMeta.CreationTimestamp.Format(time.RFC822) } -func (m meta) Labels() labels.Labels { - return labels.Set(m.ObjectMeta.Labels) +func (m meta) Labels() map[string]string { + return m.ObjectMeta.Labels +} + +// MetaNode gets the node metadata +func (m meta) MetaNode(id string) report.Node { + return report.MakeNodeWith(id, map[string]string{ + ID: m.ID(), + Name: m.Name(), + Namespace: m.Namespace(), + Created: m.Created(), + }).AddTable(LabelPrefix, m.Labels()) } diff --git a/probe/kubernetes/pod.go b/probe/kubernetes/pod.go index 9182899dad..52f53d7611 100644 --- a/probe/kubernetes/pod.go +++ b/probe/kubernetes/pod.go @@ -7,12 +7,7 @@ import ( // These constants are keys used in node metadata const ( - PodID = "kubernetes_pod_id" - PodName = "kubernetes_pod_name" - PodCreated = "kubernetes_pod_created" - PodState = "kubernetes_pod_state" - PodLabelPrefix = "kubernetes_pod_labels_" - PodIP = "kubernetes_pod_ip" + State = "kubernetes_state" StateDeleted = "deleted" ) @@ -34,7 +29,11 @@ type pod struct { // NewPod creates a new Pod func NewPod(p *api.Pod) Pod { - return &pod{Pod: p, Meta: meta{p.ObjectMeta}, parents: report.MakeSets()} + return &pod{ + Pod: p, + Meta: meta{p.ObjectMeta}, + parents: report.MakeSets(), + } } func (p *pod) UID() string { @@ -58,16 +57,11 @@ func (p *pod) NodeName() string { } func (p *pod) GetNode(probeID string) report.Node { - n := report.MakeNodeWith(report.MakePodNodeID(p.UID()), map[string]string{ - PodID: p.ID(), - PodName: p.Name(), - Namespace: p.Namespace(), - PodCreated: p.Created(), - PodState: p.State(), - PodIP: p.Status.PodIP, + return p.MetaNode(report.MakePodNodeID(p.UID())).WithLatests(map[string]string{ + State: p.State(), + IP: p.Status.PodIP, report.ControlProbeID: probeID, - }).WithParents(p.parents) - n = n.AddTable(PodLabelPrefix, p.ObjectMeta.Labels) - n = n.WithControls(GetLogs, DeletePod) - return n + }). + WithParents(p.parents). + WithControls(GetLogs, DeletePod) } diff --git a/probe/kubernetes/replica_set.go b/probe/kubernetes/replica_set.go index 3b5696b2e3..182b3d6659 100644 --- a/probe/kubernetes/replica_set.go +++ b/probe/kubernetes/replica_set.go @@ -12,54 +12,52 @@ import ( // These constants are keys used in node metadata const ( - ReplicaSetID = "kubernetes_replica_set_id" - ReplicaSetName = "kubernetes_replica_set_name" - ReplicaSetCreated = "kubernetes_replica_set_created" - ReplicaSetObservedGeneration = "kubernetes_replica_set_observed_generation" - ReplicaSetReplicas = "kubernetes_replica_set_replicas" - ReplicaSetDesiredReplicas = "kubernetes_replica_set_desired_replicas" - ReplicaSetFullyLabeledReplicas = "kubernetes_replica_set_fully_labeled_replicas" - ReplicaSetLabelPrefix = "kubernetes_replica_set_labels_" + FullyLabeledReplicas = "kubernetes_fully_labeled_replicas" ) -// ReplicaSet represents a Kubernetes replica_set +// ReplicaSet represents a Kubernetes replica set type ReplicaSet interface { Meta Selector() labels.Selector + AddParent(topology, id string) GetNode(probeID string) report.Node } type replicaSet struct { *extensions.ReplicaSet Meta - Node *api.Node + parents report.Sets + Node *api.Node } // NewReplicaSet creates a new ReplicaSet -func NewReplicaSet(d *extensions.ReplicaSet) ReplicaSet { - return &replicaSet{ReplicaSet: d, Meta: meta{d.ObjectMeta}} +func NewReplicaSet(r *extensions.ReplicaSet) ReplicaSet { + return &replicaSet{ + ReplicaSet: r, + Meta: meta{r.ObjectMeta}, + parents: report.MakeSets(), + } } -func (d *replicaSet) Selector() labels.Selector { - selector, err := unversioned.LabelSelectorAsSelector(d.Spec.Selector) +func (r *replicaSet) Selector() labels.Selector { + selector, err := unversioned.LabelSelectorAsSelector(r.Spec.Selector) if err != nil { + // TODO(paulbellamy): Remove the panic! panic(err) } return selector } -func (d *replicaSet) GetNode(probeID string) report.Node { - n := report.MakeNodeWith(report.MakeReplicaSetNodeID(d.UID()), map[string]string{ - ReplicaSetID: d.ID(), - ReplicaSetName: d.Name(), - Namespace: d.Namespace(), - ReplicaSetCreated: d.Created(), - ReplicaSetObservedGeneration: fmt.Sprint(d.Status.ObservedGeneration), - ReplicaSetReplicas: fmt.Sprint(d.Status.Replicas), - ReplicaSetDesiredReplicas: fmt.Sprint(d.Spec.Replicas), - ReplicaSetFullyLabeledReplicas: fmt.Sprint(d.Status.FullyLabeledReplicas), - report.ControlProbeID: probeID, - }) - n = n.AddTable(ReplicaSetLabelPrefix, d.ObjectMeta.Labels) - return n +func (r *replicaSet) AddParent(topology, id string) { + r.parents = r.parents.Add(topology, report.MakeStringSet(id)) +} + +func (r *replicaSet) GetNode(probeID string) report.Node { + return r.MetaNode(report.MakeReplicaSetNodeID(r.UID())).WithLatests(map[string]string{ + ObservedGeneration: fmt.Sprint(r.Status.ObservedGeneration), + Replicas: fmt.Sprint(r.Status.Replicas), + DesiredReplicas: fmt.Sprint(r.Spec.Replicas), + FullyLabeledReplicas: fmt.Sprint(r.Status.FullyLabeledReplicas), + report.ControlProbeID: probeID, + }).WithParents(r.parents) } diff --git a/probe/kubernetes/replication_controller.go b/probe/kubernetes/replication_controller.go new file mode 100644 index 0000000000..09bd32eed7 --- /dev/null +++ b/probe/kubernetes/replication_controller.go @@ -0,0 +1,46 @@ +package kubernetes + +import ( + "fmt" + + "github.com/weaveworks/scope/report" + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/labels" +) + +type replicationController struct { + *api.ReplicationController + Meta + parents report.Sets + Node *api.Node +} + +// NewReplicationController creates a new ReplicationController +func NewReplicationController(r *api.ReplicationController) ReplicaSet { + return &replicationController{ + ReplicationController: r, + Meta: meta{r.ObjectMeta}, + parents: report.MakeSets(), + } +} + +func (r *replicationController) Selector() labels.Selector { + if r.Spec.Selector == nil { + return labels.Nothing() + } + return labels.SelectorFromSet(labels.Set(r.Spec.Selector)) +} + +func (r *replicationController) AddParent(topology, id string) { + r.parents = r.parents.Add(topology, report.MakeStringSet(id)) +} + +func (r *replicationController) GetNode(probeID string) report.Node { + return r.MetaNode(report.MakeReplicaSetNodeID(r.UID())).WithLatests(map[string]string{ + ObservedGeneration: fmt.Sprint(r.Status.ObservedGeneration), + Replicas: fmt.Sprint(r.Status.Replicas), + DesiredReplicas: fmt.Sprint(r.Spec.Replicas), + FullyLabeledReplicas: fmt.Sprint(r.Status.FullyLabeledReplicas), + report.ControlProbeID: probeID, + }).WithParents(r.parents) +} diff --git a/probe/kubernetes/reporter.go b/probe/kubernetes/reporter.go index df252a7b5d..ae0b5b6aca 100644 --- a/probe/kubernetes/reporter.go +++ b/probe/kubernetes/reporter.go @@ -15,59 +15,55 @@ import ( "github.com/weaveworks/scope/report" ) +// These constants are keys used in node metadata +const ( + IP = "kubernetes_ip" + ObservedGeneration = "kubernetes_observed_generation" + Replicas = "kubernetes_replicas" + DesiredReplicas = "kubernetes_desired_replicas" +) + // Exposed for testing var ( PodMetadataTemplates = report.MetadataTemplates{ - PodID: {ID: PodID, Label: "ID", From: report.FromLatest, Priority: 1}, - PodState: {ID: PodState, Label: "State", From: report.FromLatest, Priority: 2}, - PodIP: {ID: PodIP, Label: "IP", From: report.FromLatest, Priority: 3}, + ID: {ID: ID, Label: "ID", From: report.FromLatest, Priority: 1}, + State: {ID: State, Label: "State", From: report.FromLatest, Priority: 2}, + IP: {ID: IP, Label: "IP", From: report.FromLatest, Priority: 3}, report.Container: {ID: report.Container, Label: "# Containers", From: report.FromCounters, Datatype: "number", Priority: 4}, Namespace: {ID: Namespace, Label: "Namespace", From: report.FromLatest, Priority: 5}, - PodCreated: {ID: PodCreated, Label: "Created", From: report.FromLatest, Priority: 6}, + Created: {ID: Created, Label: "Created", From: report.FromLatest, Priority: 6}, } ServiceMetadataTemplates = report.MetadataTemplates{ - ServiceID: {ID: ServiceID, Label: "ID", From: report.FromLatest, Priority: 1}, - Namespace: {ID: Namespace, Label: "Namespace", From: report.FromLatest, Priority: 2}, - ServiceCreated: {ID: ServiceCreated, Label: "Created", From: report.FromLatest, Priority: 3}, - ServicePublicIP: {ID: ServicePublicIP, Label: "Public IP", From: report.FromLatest, Priority: 4}, - ServiceIP: {ID: ServiceIP, Label: "Internal IP", From: report.FromLatest, Priority: 5}, - report.Pod: {ID: report.Pod, Label: "# Pods", From: report.FromCounters, Datatype: "number", Priority: 6}, + ID: {ID: ID, Label: "ID", From: report.FromLatest, Priority: 1}, + Namespace: {ID: Namespace, Label: "Namespace", From: report.FromLatest, Priority: 2}, + Created: {ID: Created, Label: "Created", From: report.FromLatest, Priority: 3}, + PublicIP: {ID: PublicIP, Label: "Public IP", From: report.FromLatest, Priority: 4}, + IP: {ID: IP, Label: "Internal IP", From: report.FromLatest, Priority: 5}, + report.Pod: {ID: report.Pod, Label: "# Pods", From: report.FromCounters, Datatype: "number", Priority: 6}, } DeploymentMetadataTemplates = report.MetadataTemplates{ - DeploymentID: {ID: DeploymentID, Label: "ID", From: report.FromLatest, Priority: 1}, - Namespace: {ID: Namespace, Label: "Namespace", From: report.FromLatest, Priority: 2}, - DeploymentCreated: {ID: DeploymentCreated, Label: "Created", From: report.FromLatest, Priority: 3}, - DeploymentObservedGeneration: {ID: DeploymentObservedGeneration, Label: "Observed Gen.", From: report.FromLatest, Priority: 4}, - DeploymentDesiredReplicas: {ID: DeploymentDesiredReplicas, Label: "Desired Replicas", From: report.FromLatest, Datatype: "number", Priority: 5}, - report.Pod: {ID: report.Pod, Label: "# Pods", From: report.FromCounters, Datatype: "number", Priority: 6}, - DeploymentStrategy: {ID: DeploymentStrategy, Label: "Strategy", From: report.FromLatest, Priority: 7}, + ID: {ID: ID, Label: "ID", From: report.FromLatest, Priority: 1}, + Namespace: {ID: Namespace, Label: "Namespace", From: report.FromLatest, Priority: 2}, + Created: {ID: Created, Label: "Created", From: report.FromLatest, Priority: 3}, + ObservedGeneration: {ID: ObservedGeneration, Label: "Observed Gen.", From: report.FromLatest, Priority: 4}, + DesiredReplicas: {ID: DesiredReplicas, Label: "Desired Replicas", From: report.FromLatest, Datatype: "number", Priority: 5}, + report.Pod: {ID: report.Pod, Label: "# Pods", From: report.FromCounters, Datatype: "number", Priority: 6}, + Strategy: {ID: Strategy, Label: "Strategy", From: report.FromLatest, Priority: 7}, } ReplicaSetMetadataTemplates = report.MetadataTemplates{ - ReplicaSetID: {ID: ReplicaSetID, Label: "ID", From: report.FromLatest, Priority: 1}, - Namespace: {ID: Namespace, Label: "Namespace", From: report.FromLatest, Priority: 2}, - ReplicaSetCreated: {ID: ReplicaSetCreated, Label: "Created", From: report.FromLatest, Priority: 3}, - ReplicaSetObservedGeneration: {ID: ReplicaSetObservedGeneration, Label: "Observed Gen.", From: report.FromLatest, Priority: 4}, - ReplicaSetDesiredReplicas: {ID: ReplicaSetDesiredReplicas, Label: "Desired Replicas", From: report.FromLatest, Datatype: "number", Priority: 5}, - report.Pod: {ID: report.Pod, Label: "# Pods", From: report.FromCounters, Datatype: "number", Priority: 6}, - } - - PodTableTemplates = report.TableTemplates{ - PodLabelPrefix: {ID: PodLabelPrefix, Label: "Kubernetes Labels", Prefix: PodLabelPrefix}, - } - - ServiceTableTemplates = report.TableTemplates{ - ServiceLabelPrefix: {ID: ServiceLabelPrefix, Label: "Kubernetes Labels", Prefix: ServiceLabelPrefix}, - } - - DeploymentTableTemplates = report.TableTemplates{ - DeploymentLabelPrefix: {ID: DeploymentLabelPrefix, Label: "Kubernetes Labels", Prefix: DeploymentLabelPrefix}, + ID: {ID: ID, Label: "ID", From: report.FromLatest, Priority: 1}, + Namespace: {ID: Namespace, Label: "Namespace", From: report.FromLatest, Priority: 2}, + Created: {ID: Created, Label: "Created", From: report.FromLatest, Priority: 3}, + ObservedGeneration: {ID: ObservedGeneration, Label: "Observed Gen.", From: report.FromLatest, Priority: 4}, + DesiredReplicas: {ID: DesiredReplicas, Label: "Desired Replicas", From: report.FromLatest, Datatype: "number", Priority: 5}, + report.Pod: {ID: report.Pod, Label: "# Pods", From: report.FromCounters, Datatype: "number", Priority: 6}, } - ReplicaSetTableTemplates = report.TableTemplates{ - ReplicaSetLabelPrefix: {ID: ReplicaSetLabelPrefix, Label: "Kubernetes Labels", Prefix: ReplicaSetLabelPrefix}, + TableTemplates = report.TableTemplates{ + LabelPrefix: {ID: LabelPrefix, Label: "Kubernetes Labels", Prefix: LabelPrefix}, } ) @@ -113,7 +109,7 @@ func (r *Reporter) podEvent(e Event, pod Pod) { rpt.Pod.AddNode( report.MakeNodeWith( report.MakePodNodeID(pod.UID()), - map[string]string{PodState: StateDeleted}, + map[string]string{State: StateDeleted}, ), ) r.probe.Publish(rpt) @@ -173,11 +169,11 @@ func (r *Reporter) Report() (report.Report, error) { if err != nil { return result, err } - replicaSetTopology, replicaSets, err := r.replicaSetTopology(r.probeID) + replicaSetTopology, replicaSets, err := r.replicaSetTopology(r.probeID, deployments) if err != nil { return result, err } - podTopology, err := r.podTopology(services, deployments, replicaSets) + podTopology, err := r.podTopology(services, replicaSets) if err != nil { return result, err } @@ -192,7 +188,7 @@ func (r *Reporter) serviceTopology() (report.Topology, []Service, error) { var ( result = report.MakeTopology(). WithMetadataTemplates(ServiceMetadataTemplates). - WithTableTemplates(ServiceTableTemplates) + WithTableTemplates(TableTemplates) services = []Service{} ) err := r.client.WalkServices(func(s Service) error { @@ -207,7 +203,7 @@ func (r *Reporter) deploymentTopology(probeID string) (report.Topology, []Deploy var ( result = report.MakeTopology(). WithMetadataTemplates(DeploymentMetadataTemplates). - WithTableTemplates(DeploymentTableTemplates) + WithTableTemplates(TableTemplates) deployments = []Deployment{} ) err := r.client.WalkDeployments(func(d Deployment) error { @@ -218,14 +214,26 @@ func (r *Reporter) deploymentTopology(probeID string) (report.Topology, []Deploy return result, deployments, err } -func (r *Reporter) replicaSetTopology(probeID string) (report.Topology, []ReplicaSet, error) { +func (r *Reporter) replicaSetTopology(probeID string, deployments []Deployment) (report.Topology, []ReplicaSet, error) { var ( result = report.MakeTopology(). WithMetadataTemplates(ReplicaSetMetadataTemplates). - WithTableTemplates(ReplicaSetTableTemplates) + WithTableTemplates(TableTemplates) replicaSets = []ReplicaSet{} + selectors = []func(labelledChild){} ) + for _, deployment := range deployments { + selectors = append(selectors, match( + deployment.Selector(), + report.Deployment, + report.MakeDeploymentNodeID(deployment.UID()), + )) + } + err := r.client.WalkReplicaSets(func(r ReplicaSet) error { + for _, selector := range selectors { + selector(r) + } result = result.AddNode(r.GetNode(probeID)) replicaSets = append(replicaSets, r) return nil @@ -254,19 +262,26 @@ var GetNodeName = func(r *Reporter) (string, error) { return nodeName, err } -func (r *Reporter) podTopology(services []Service, deployments []Deployment, replicaSets []ReplicaSet) (report.Topology, error) { +type labelledChild interface { + Labels() map[string]string + AddParent(string, string) +} + +// Match parses the selectors and adds the target as a parent if the selector matches. +func match(selector labels.Selector, topology, id string) func(labelledChild) { + return func(c labelledChild) { + if selector.Matches(labels.Set(c.Labels())) { + c.AddParent(topology, id) + } + } +} + +func (r *Reporter) podTopology(services []Service, replicaSets []ReplicaSet) (report.Topology, error) { var ( pods = report.MakeTopology(). WithMetadataTemplates(PodMetadataTemplates). - WithTableTemplates(PodTableTemplates) - selectors = []func(Pod){} - match = func(selector labels.Selector, topology, id string) func(Pod) { - return func(p Pod) { - if selector.Matches(p.Labels()) { - p.AddParent(topology, id) - } - } - } + WithTableTemplates(TableTemplates) + selectors = []func(labelledChild){} ) pods.Controls.AddControl(report.Control{ ID: GetLogs, @@ -287,13 +302,6 @@ func (r *Reporter) podTopology(services []Service, deployments []Deployment, rep report.MakeServiceNodeID(service.UID()), )) } - for _, deployment := range deployments { - selectors = append(selectors, match( - deployment.Selector(), - report.Deployment, - report.MakeDeploymentNodeID(deployment.UID()), - )) - } for _, replicaSet := range replicaSets { selectors = append(selectors, match( replicaSet.Selector(), diff --git a/probe/kubernetes/reporter_test.go b/probe/kubernetes/reporter_test.go index c8c19d7c0a..1a96e999d5 100644 --- a/probe/kubernetes/reporter_test.go +++ b/probe/kubernetes/reporter_test.go @@ -184,16 +184,16 @@ func TestReporter(t *testing.T) { latest map[string]string }{ {pod1ID, serviceID, map[string]string{ - kubernetes.PodID: "ping/pong-a", - kubernetes.PodName: "pong-a", - kubernetes.Namespace: "ping", - kubernetes.PodCreated: pod1.Created(), + kubernetes.ID: "ping/pong-a", + kubernetes.Name: "pong-a", + kubernetes.Namespace: "ping", + kubernetes.Created: pod1.Created(), }}, {pod2ID, serviceID, map[string]string{ - kubernetes.PodID: "ping/pong-b", - kubernetes.PodName: "pong-b", - kubernetes.Namespace: "ping", - kubernetes.PodCreated: pod1.Created(), + kubernetes.ID: "ping/pong-b", + kubernetes.Name: "pong-b", + kubernetes.Namespace: "ping", + kubernetes.Created: pod1.Created(), }}, } { node, ok := rpt.Pod.Nodes[pod.id] @@ -220,10 +220,10 @@ func TestReporter(t *testing.T) { } for k, want := range map[string]string{ - kubernetes.ServiceID: "ping/pongservice", - kubernetes.ServiceName: "pongservice", - kubernetes.Namespace: "ping", - kubernetes.ServiceCreated: pod1.Created(), + kubernetes.ID: "ping/pongservice", + kubernetes.Name: "pongservice", + kubernetes.Namespace: "ping", + kubernetes.Created: pod1.Created(), } { if have, ok := node.Latest.Lookup(k); !ok || have != want { t.Errorf("Expected service %s latest %q: %q, got %q", serviceID, k, want, have) diff --git a/probe/kubernetes/service.go b/probe/kubernetes/service.go index c47c68d234..e758d4248e 100644 --- a/probe/kubernetes/service.go +++ b/probe/kubernetes/service.go @@ -8,12 +8,7 @@ import ( // These constants are keys used in node metadata const ( - ServiceID = "kubernetes_service_id" - ServiceName = "kubernetes_service_name" - ServiceCreated = "kubernetes_service_created" - ServiceIP = "kubernetes_service_ip" - ServicePublicIP = "kubernetes_service_public_ip" - ServiceLabelPrefix = "kubernetes_service_label_" + PublicIP = "kubernetes_public_ip" ) // Service represents a Kubernetes service @@ -41,19 +36,9 @@ func (s *service) Selector() labels.Selector { } func (s *service) GetNode() report.Node { - latest := map[string]string{ - ServiceID: s.ID(), - ServiceName: s.Name(), - ServiceCreated: s.Created(), - Namespace: s.Namespace(), - ServiceIP: s.Spec.ClusterIP, - } + latest := map[string]string{IP: s.Spec.ClusterIP} if s.Spec.LoadBalancerIP != "" { - latest[ServicePublicIP] = s.Spec.LoadBalancerIP + latest[PublicIP] = s.Spec.LoadBalancerIP } - return report.MakeNodeWith( - report.MakeServiceNodeID(s.UID()), - latest, - ). - AddTable(ServiceLabelPrefix, s.ObjectMeta.Labels) + return s.MetaNode(report.MakeServiceNodeID(s.UID())).WithLatests(latest) } diff --git a/render/detailed/node.go b/render/detailed/node.go index 10a0373535..f959f79c90 100644 --- a/render/detailed/node.go +++ b/render/detailed/node.go @@ -141,25 +141,36 @@ var ( }, }, { - topologyID: report.Pod, + topologyID: report.Service, NodeSummaryGroup: NodeSummaryGroup{ - TopologyID: "pods", - Label: "Pods", - + TopologyID: "pods-by-service", + Label: "Services", Columns: []Column{ - {ID: report.Container, Label: "# Containers"}, - {ID: kubernetes.PodIP, Label: "IP"}, + {ID: report.Pod, Label: "# Pods"}, + {ID: kubernetes.IP, Label: "IP"}, }, }, }, { - topologyID: report.Service, + topologyID: report.ReplicaSet, NodeSummaryGroup: NodeSummaryGroup{ - TopologyID: "pods-by-service", - Label: "Services", + TopologyID: "replica-sets", + Label: "Replica Sets", Columns: []Column{ {ID: report.Pod, Label: "# Pods"}, - {ID: kubernetes.ServiceIP, Label: "IP"}, + {ID: kubernetes.ObservedGeneration, Label: "Observed Gen."}, + }, + }, + }, + { + topologyID: report.Pod, + NodeSummaryGroup: NodeSummaryGroup{ + TopologyID: "pods", + Label: "Pods", + + Columns: []Column{ + {ID: report.Container, Label: "# Containers"}, + {ID: kubernetes.IP, Label: "IP"}, }, }, }, diff --git a/render/detailed/node_test.go b/render/detailed/node_test.go index 62acaec754..97f0887894 100644 --- a/render/detailed/node_test.go +++ b/render/detailed/node_test.go @@ -100,7 +100,7 @@ func TestMakeDetailedHostNode(t *testing.T) { TopologyID: "pods", Columns: []detailed.Column{ {ID: report.Container, Label: "# Containers"}, - {ID: kubernetes.PodIP, Label: "IP"}, + {ID: kubernetes.IP, Label: "IP"}, }, Nodes: []detailed.NodeSummary{podNodeSummary}, }, @@ -327,8 +327,8 @@ func TestMakeDetailedPodNode(t *testing.T) { Linkable: true, Pseudo: false, Metadata: []report.MetadataRow{ - {ID: "kubernetes_pod_id", Label: "ID", Value: "ping/pong-b", Priority: 1}, - {ID: "kubernetes_pod_state", Label: "State", Value: "running", Priority: 2}, + {ID: "kubernetes_id", Label: "ID", Value: "ping/pong-b", Priority: 1}, + {ID: "kubernetes_state", Label: "State", Value: "running", Priority: 2}, {ID: "container", Label: "# Containers", Value: "1", Priority: 4, Datatype: "number"}, {ID: "kubernetes_namespace", Label: "Namespace", Value: "ping", Priority: 5}, }, diff --git a/render/detailed/parents.go b/render/detailed/parents.go index d813acce3e..eb101bbd34 100644 --- a/render/detailed/parents.go +++ b/render/detailed/parents.go @@ -63,7 +63,7 @@ func containerParent(n report.Node) Parent { } func podParent(n report.Node) Parent { - podName, _ := n.Latest.Lookup(kubernetes.PodName) + podName, _ := n.Latest.Lookup(kubernetes.Name) return Parent{ ID: n.ID, Label: podName, @@ -72,7 +72,7 @@ func podParent(n report.Node) Parent { } func serviceParent(n report.Node) Parent { - serviceName, _ := n.Latest.Lookup(kubernetes.ServiceName) + serviceName, _ := n.Latest.Lookup(kubernetes.Name) return Parent{ ID: n.ID, Label: serviceName, diff --git a/render/detailed/summary.go b/render/detailed/summary.go index 099e2a1771..a78f52436d 100644 --- a/render/detailed/summary.go +++ b/render/detailed/summary.go @@ -240,8 +240,8 @@ func containerImageNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bo } func podNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) { - base.Label, _ = n.Latest.Lookup(kubernetes.PodName) - base.Rank, _ = n.Latest.Lookup(kubernetes.PodID) + base.Label, _ = n.Latest.Lookup(kubernetes.Name) + base.Rank, _ = n.Latest.Lookup(kubernetes.ID) if c, ok := n.Counters.Lookup(report.Container); ok { if c == 1 { @@ -255,8 +255,8 @@ func podNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) { } func serviceNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) { - base.Label, _ = n.Latest.Lookup(kubernetes.ServiceName) - base.Rank, _ = n.Latest.Lookup(kubernetes.ServiceID) + base.Label, _ = n.Latest.Lookup(kubernetes.Name) + base.Rank, _ = n.Latest.Lookup(kubernetes.ID) base.Stack = true // Services are always just a group of pods, so there's no counting multiple @@ -273,8 +273,8 @@ func serviceNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) { } func deploymentNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) { - base.Label, _ = n.Latest.Lookup(kubernetes.DeploymentName) - base.Rank, _ = n.Latest.Lookup(kubernetes.DeploymentID) + base.Label, _ = n.Latest.Lookup(kubernetes.Name) + base.Rank, _ = n.Latest.Lookup(kubernetes.ID) base.Stack = true if p, ok := n.Counters.Lookup(report.Pod); ok { @@ -289,8 +289,8 @@ func deploymentNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) } func replicaSetNodeSummary(base NodeSummary, n report.Node) (NodeSummary, bool) { - base.Label, _ = n.Latest.Lookup(kubernetes.ReplicaSetName) - base.Rank, _ = n.Latest.Lookup(kubernetes.ReplicaSetID) + base.Label, _ = n.Latest.Lookup(kubernetes.Name) + base.Rank, _ = n.Latest.Lookup(kubernetes.ID) base.Stack = true if p, ok := n.Counters.Lookup(report.Pod); ok { diff --git a/render/pod.go b/render/pod.go index a057ff3d7b..3bc7434339 100644 --- a/render/pod.go +++ b/render/pod.go @@ -23,7 +23,7 @@ func renderKubernetesTopologies(rpt report.Report) bool { var PodRenderer = ConditionalRenderer(renderKubernetesTopologies, ApplyDecorators(MakeFilter( func(n report.Node) bool { - state, ok := n.Latest.Lookup(kubernetes.PodState) + state, ok := n.Latest.Lookup(kubernetes.State) return (!ok || state != kubernetes.StateDeleted) }, MakeReduce( @@ -42,7 +42,7 @@ var PodServiceRenderer = ConditionalRenderer(renderKubernetesTopologies, ApplyDecorators(FilterEmpty(report.Pod, MakeReduce( MakeMap( - MapPod2Service, + Map2Service, PodRenderer, ), SelectService, @@ -56,8 +56,8 @@ var DeploymentRenderer = ApplyDecorators( FilterEmpty(report.Pod, MakeReduce( MakeMap( - MapPod2Deployment, - PodRenderer, + Map2Deployment, + ReplicaSetRenderer, ), SelectDeployment, ), @@ -70,7 +70,7 @@ var ReplicaSetRenderer = ApplyDecorators( FilterEmpty(report.Pod, MakeReduce( MakeMap( - MapPod2ReplicaSet, + Map2ReplicaSet, PodRenderer, ), SelectReplicaSet, @@ -125,31 +125,31 @@ func MapContainer2Pod(n report.Node, _ report.Networks) report.Nodes { // The various ways of grouping pods var ( - MapPod2Service = MapPod2Parent(report.Service) - MapPod2Deployment = MapPod2Parent(report.Deployment) - MapPod2ReplicaSet = MapPod2Parent(report.ReplicaSet) + Map2Service = Map2Parent(report.Service) + Map2Deployment = Map2Parent(report.Deployment) + Map2ReplicaSet = Map2Parent(report.ReplicaSet) ) -// MapPod2Parent maps pod Nodes to some kubernetes grouping. -func MapPod2Parent(topology string) func(pod report.Node, _ report.Networks) report.Nodes { - return func(pod report.Node, _ report.Networks) report.Nodes { +// Map2Parent maps Nodes to some parent grouping. +func Map2Parent(topology string) func(n report.Node, _ report.Networks) report.Nodes { + return func(n report.Node, _ report.Networks) report.Nodes { // Propagate all pseudo nodes - if pod.Topology == Pseudo { - return report.Nodes{pod.ID: pod} + if n.Topology == Pseudo { + return report.Nodes{n.ID: n} } - // Otherwise, if some some reason the pod doesn't have any of these ids + // Otherwise, if some some reason the node 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.Parents.Lookup(topology) + groupIDs, ok := n.Parents.Lookup(topology) if !ok { return report.Nodes{} } result := report.Nodes{} for _, id := range groupIDs { - node := NewDerivedNode(id, pod).WithTopology(topology) - node.Counters = node.Counters.Add(pod.Topology, 1) + node := NewDerivedNode(id, n).WithTopology(topology) + node.Counters = node.Counters.Add(n.Topology, 1) result[id] = node } return result diff --git a/test/fixture/report_fixture.go b/test/fixture/report_fixture.go index 4c54d7033b..2951b4a89c 100644 --- a/test/fixture/report_fixture.go +++ b/test/fixture/report_fixture.go @@ -369,8 +369,8 @@ var ( Nodes: report.Nodes{ ClientPodNodeID: report.MakeNodeWith( ClientPodNodeID, map[string]string{ - kubernetes.PodID: ClientPodID, - kubernetes.PodName: "pong-a", + kubernetes.ID: ClientPodID, + kubernetes.Name: "pong-a", kubernetes.Namespace: KubernetesNamespace, report.HostNodeID: ClientHostNodeID, }). @@ -380,10 +380,10 @@ var ( ), ServerPodNodeID: report.MakeNodeWith( ServerPodNodeID, map[string]string{ - kubernetes.PodID: ServerPodID, - kubernetes.PodName: "pong-b", + kubernetes.ID: ServerPodID, + kubernetes.Name: "pong-b", kubernetes.Namespace: KubernetesNamespace, - kubernetes.PodState: "running", + kubernetes.State: "running", report.HostNodeID: ServerHostNodeID, }). WithTopology(report.Pod).WithParents(report.EmptySets. @@ -398,9 +398,9 @@ var ( ServiceNodeID: report.MakeNodeWith( ServiceNodeID, map[string]string{ - kubernetes.ServiceID: ServiceID, - kubernetes.ServiceName: "pongservice", - kubernetes.Namespace: "ping", + kubernetes.ID: ServiceID, + kubernetes.Name: "pongservice", + kubernetes.Namespace: "ping", }). WithTopology(report.Service), }, From d0633d0d8003404db7c8c028ecf84d56ed11443f Mon Sep 17 00:00:00 2001 From: Paul Bellamy Date: Fri, 6 May 2016 15:41:36 +0100 Subject: [PATCH 4/4] Render replica set and deployment parents --- render/detailed/node.go | 2 +- render/detailed/node_test.go | 2 +- render/detailed/parents.go | 30 ++++++++++++++++-------------- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/render/detailed/node.go b/render/detailed/node.go index f959f79c90..590e6e8874 100644 --- a/render/detailed/node.go +++ b/render/detailed/node.go @@ -143,7 +143,7 @@ var ( { topologyID: report.Service, NodeSummaryGroup: NodeSummaryGroup{ - TopologyID: "pods-by-service", + TopologyID: "services", Label: "Services", Columns: []Column{ {ID: report.Pod, Label: "# Pods"}, diff --git a/render/detailed/node_test.go b/render/detailed/node_test.go index 97f0887894..ed4388b087 100644 --- a/render/detailed/node_test.go +++ b/render/detailed/node_test.go @@ -364,7 +364,7 @@ func TestMakeDetailedPodNode(t *testing.T) { { ID: fixture.ServiceNodeID, Label: fixture.ServiceName, - TopologyID: "pods-by-service", + TopologyID: "services", }, }, Connections: []detailed.ConnectionsSummary{ diff --git a/render/detailed/parents.go b/render/detailed/parents.go index eb101bbd34..4094db74e5 100644 --- a/render/detailed/parents.go +++ b/render/detailed/parents.go @@ -25,6 +25,8 @@ func Parents(r report.Report, n report.Node) (result []Parent) { }{ report.Container: {r.Container, containerParent}, report.Pod: {r.Pod, podParent}, + report.ReplicaSet: {r.ReplicaSet, replicaSetParent}, + report.Deployment: {r.Deployment, deploymentParent}, report.Service: {r.Service, serviceParent}, report.ContainerImage: {r.ContainerImage, containerImageParent}, report.Host: {r.Host, hostParent}, @@ -62,21 +64,21 @@ func containerParent(n report.Node) Parent { } } -func podParent(n report.Node) Parent { - podName, _ := n.Latest.Lookup(kubernetes.Name) - return Parent{ - ID: n.ID, - Label: podName, - TopologyID: "pods", - } -} +var ( + podParent = kubernetesParent("pods") + replicaSetParent = kubernetesParent("replica-sets") + deploymentParent = kubernetesParent("deployments") + serviceParent = kubernetesParent("services") +) -func serviceParent(n report.Node) Parent { - serviceName, _ := n.Latest.Lookup(kubernetes.Name) - return Parent{ - ID: n.ID, - Label: serviceName, - TopologyID: "pods-by-service", +func kubernetesParent(topology string) func(report.Node) Parent { + return func(n report.Node) Parent { + name, _ := n.Latest.Lookup(kubernetes.Name) + return Parent{ + ID: n.ID, + Label: name, + TopologyID: topology, + } } }