mirror of
https://github.com/weaveworks/scope.git
synced 2026-07-16 03:49:52 +00:00
Deployment and ReplicaSet views for k8s
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
71
probe/kubernetes/deployment.go
Normal file
71
probe/kubernetes/deployment.go
Normal 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
46
probe/kubernetes/meta.go
Normal 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)
|
||||
}
|
||||
@@ -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
|
||||
|
||||
65
probe/kubernetes/replica_set.go
Normal file
65
probe/kubernetes/replica_set.go
Normal 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
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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{
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user