Deployment and ReplicaSet views for k8s

This commit is contained in:
Paul Bellamy
2016-05-04 17:03:05 +01:00
parent 0178babb0e
commit 16a5c738d9
17 changed files with 488 additions and 62 deletions

View File

@@ -81,13 +81,6 @@ func init() {
Rank: 2,
Options: containerFilters,
},
APITopologyDesc{
id: "containers-by-image",
parent: "containers",
renderer: render.ContainerImageRenderer,
Name: "by image",
Options: containerFilters,
},
APITopologyDesc{
id: "containers-by-hostname",
parent: "containers",
@@ -95,6 +88,13 @@ func init() {
Name: "by DNS name",
Options: containerFilters,
},
APITopologyDesc{
id: "containers-by-image",
parent: "containers",
renderer: render.ContainerImageRenderer,
Name: "by image",
Options: containerFilters,
},
APITopologyDesc{
id: "pods",
renderer: render.PodRenderer,
@@ -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
}

View File

@@ -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

View File

@@ -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 {

View File

@@ -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
}

46
probe/kubernetes/meta.go Normal file
View File

@@ -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)
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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{
{

View File

@@ -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)

View File

@@ -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

View File

@@ -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)
)

View File

@@ -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

View File

@@ -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]

View File

@@ -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,