Merge pull request #1436 from weaveworks/1372-deployment-view

Deployment and Replica Set views
This commit is contained in:
Paul Bellamy
2016-05-09 09:44:49 +01:00
21 changed files with 723 additions and 307 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,9 +150,9 @@ 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 {
if state, ok := n.Latest.Lookup(kubernetes.State); ok && state == kubernetes.StateDeleted {
continue
}
if namespace, ok := n.Latest.Lookup(kubernetes.Namespace); ok {
@@ -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"
@@ -16,16 +17,13 @@ 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()
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 +33,15 @@ 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{}
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)
@@ -80,32 +79,33 @@ func NewClient(addr string, resyncPeriod time.Duration) (Client, error) {
return nil, err
}
result := &client{
quit: make(chan struct{}),
client: c,
ec, err := unversioned.NewExtensions(config)
if err != nil {
return nil, err
}
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)
result := &client{
quit: make(chan struct{}),
resyncPeriod: resyncPeriod,
client: c,
}
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)
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.nodeReflector, resyncPeriod, result.quit)
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()
@@ -146,6 +146,47 @@ 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
}
// 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
}
for i := range list {
if err := f(NewReplicaSet(&(list[i]))); err != nil {
return err
}
}
}
{
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
}
func (c *client) WalkNodes(f func(*api.Node) error) error {
list, err := c.nodeStore.List()
if err != nil {

View File

@@ -0,0 +1,59 @@
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 (
UpdatedReplicas = "kubernetes_updated_replicas"
AvailableReplicas = "kubernetes_available_replicas"
UnavailableReplicas = "kubernetes_unavailable_replicas"
Strategy = "kubernetes_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 {
// TODO(paulbellamy): Remove the panic!
panic(err)
}
return selector
}
func (d *deployment) GetNode(probeID string) report.Node {
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,
})
}

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

@@ -0,0 +1,67 @@
package kubernetes
import (
"time"
"k8s.io/kubernetes/pkg/api"
"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
type Meta interface {
UID() string
ID() string
Name() string
Namespace() string
Created() string
Labels() map[string]string
MetaNode(id string) report.Node
}
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() 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())
}

View File

@@ -1,49 +1,39 @@
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
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"
ServiceIDs = "kubernetes_service_ids"
State = "kubernetes_state"
StateDeleted = "deleted"
)
// Pod represents a Kubernetes pod
type Pod interface {
UID() string
ID() string
Name() string
Namespace() string
Created() string
AddServiceID(id string)
Labels() labels.Labels
Meta
AddParent(topology, id string)
NodeName() string
GetNode(probeID string) report.Node
}
type pod struct {
*api.Pod
serviceIDs report.StringSet
Node *api.Node
Meta
parents report.Sets
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},
parents: report.MakeSets(),
}
}
func (p *pod) UID() string {
@@ -51,31 +41,11 @@ func (p *pod) UID() string {
if hash, ok := p.ObjectMeta.Annotations["kubernetes.io/config.hash"]; ok {
return hash
}
return string(p.ObjectMeta.UID)
return p.Meta.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)
}
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 {
@@ -87,25 +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,
}).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]))),
)
}
n = n.AddTable(PodLabelPrefix, p.ObjectMeta.Labels)
n = n.WithControls(GetLogs, DeletePod)
return n
}).
WithParents(p.parents).
WithControls(GetLogs, DeletePod)
}

View File

@@ -0,0 +1,63 @@
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 (
FullyLabeledReplicas = "kubernetes_fully_labeled_replicas"
)
// 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
parents report.Sets
Node *api.Node
}
// NewReplicaSet creates a new ReplicaSet
func NewReplicaSet(r *extensions.ReplicaSet) ReplicaSet {
return &replicaSet{
ReplicaSet: r,
Meta: meta{r.ObjectMeta},
parents: report.MakeSets(),
}
}
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 (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)
}

View File

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

View File

@@ -15,32 +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},
}
PodTableTemplates = report.TableTemplates{
PodLabelPrefix: {ID: PodLabelPrefix, Label: "Kubernetes Labels", Prefix: PodLabelPrefix},
DeploymentMetadataTemplates = report.MetadataTemplates{
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},
}
ServiceTableTemplates = report.TableTemplates{
ServiceLabelPrefix: {ID: ServiceLabelPrefix, Label: "Kubernetes Labels", Prefix: ServiceLabelPrefix},
ReplicaSetMetadataTemplates = report.MetadataTemplates{
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},
}
TableTemplates = report.TableTemplates{
LabelPrefix: {ID: LabelPrefix, Label: "Kubernetes Labels", Prefix: LabelPrefix},
}
)
@@ -86,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)
@@ -142,12 +165,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, deployments)
if err != nil {
return result, err
}
podTopology, err := r.podTopology(services, 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
}
@@ -155,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 {
@@ -166,6 +199,48 @@ 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(TableTemplates)
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, deployments []Deployment) (report.Topology, []ReplicaSet, error) {
var (
result = report.MakeTopology().
WithMetadataTemplates(ReplicaSetMetadataTemplates).
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
})
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,12 +262,26 @@ var GetNodeName = func(r *Reporter) (string, error) {
return nodeName, err
}
func (r *Reporter) podTopology(services []Service) (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 = map[string]labels.Selector{}
WithTableTemplates(TableTemplates)
selectors = []func(labelledChild){}
)
pods.Controls.AddControl(report.Control{
ID: GetLogs,
@@ -207,7 +296,18 @@ 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(),
report.Service,
report.MakeServiceNodeID(service.UID()),
))
}
for _, replicaSet := range replicaSets {
selectors = append(selectors, match(
replicaSet.Selector(),
report.ReplicaSet,
report.MakeReplicaSetNodeID(replicaSet.UID()),
))
}
thisNodeName, err := GetNodeName(r)
@@ -218,10 +318,8 @@ func (r *Reporter) podTopology(services []Service) (report.Topology, error) {
if p.NodeName() != thisNodeName {
return nil
}
for serviceID, selector := range selectors {
if selector.Matches(p.Labels()) {
p.AddServiceID(serviceID)
}
for _, selector := range selectors {
selector(p)
}
pods = pods.AddNode(p.GetNode(r.probeID))
return nil

View File

@@ -22,6 +22,7 @@ var (
nodeName = "nodename"
pod1UID = "a1b2c3d4e5"
pod2UID = "f6g7h8i9j0"
serviceUID = "service1234"
podTypeMeta = unversioned.TypeMeta{
Kind: "Pod",
APIVersion: "v1",
@@ -73,6 +74,7 @@ var (
},
ObjectMeta: api.ObjectMeta{
Name: "pongservice",
UID: types.UID(serviceUID),
Namespace: "ping",
CreationTimestamp: unversioned.Now(),
},
@@ -129,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
}
@@ -166,7 +174,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
@@ -174,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("ping/pongservice"),
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(),
}, map[string]report.StringSet{
kubernetes.ServiceIDs: report.MakeStringSet("ping/pongservice"),
kubernetes.ID: "ping/pong-b",
kubernetes.Name: "pong-b",
kubernetes.Namespace: "ping",
kubernetes.Created: pod1.Created(),
}},
} {
node, ok := rpt.Pod.Nodes[pod.id]
@@ -207,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
@@ -223,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)

View File

@@ -1,8 +1,6 @@
package kubernetes
import (
"time"
"github.com/weaveworks/scope/report"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/labels"
@@ -10,47 +8,24 @@ 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
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 {
@@ -61,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.ObjectMeta.CreationTimestamp.Format(time.RFC822),
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.Namespace(), s.Name()),
latest,
).
AddTable(ServiceLabelPrefix, s.Labels)
return s.MetaNode(report.MakeServiceNodeID(s.UID())).WithLatests(latest)
}

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

@@ -140,6 +140,28 @@ var (
},
},
},
{
topologyID: report.Service,
NodeSummaryGroup: NodeSummaryGroup{
TopologyID: "services",
Label: "Services",
Columns: []Column{
{ID: report.Pod, Label: "# Pods"},
{ID: kubernetes.IP, Label: "IP"},
},
},
},
{
topologyID: report.ReplicaSet,
NodeSummaryGroup: NodeSummaryGroup{
TopologyID: "replica-sets",
Label: "Replica Sets",
Columns: []Column{
{ID: report.Pod, Label: "# Pods"},
{ID: kubernetes.ObservedGeneration, Label: "Observed Gen."},
},
},
},
{
topologyID: report.Pod,
NodeSummaryGroup: NodeSummaryGroup{
@@ -148,18 +170,7 @@ var (
Columns: []Column{
{ID: report.Container, Label: "# Containers"},
{ID: kubernetes.PodIP, Label: "IP"},
},
},
},
{
topologyID: report.Service,
NodeSummaryGroup: NodeSummaryGroup{
TopologyID: "pods-by-service",
Label: "Services",
Columns: []Column{
{ID: report.Pod, Label: "# Pods"},
{ID: kubernetes.ServiceIP, Label: "IP"},
{ID: kubernetes.IP, Label: "IP"},
},
},
},

View File

@@ -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},
},
@@ -361,6 +361,11 @@ func TestMakeDetailedPodNode(t *testing.T) {
Label: fixture.ServerHostName,
TopologyID: "hosts",
},
{
ID: fixture.ServiceNodeID,
Label: fixture.ServiceName,
TopologyID: "services",
},
},
Connections: []detailed.ConnectionsSummary{
{

View File

@@ -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.PodName)
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.ServiceName)
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,
}
}
}

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 {
@@ -238,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 {
@@ -253,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
@@ -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.Name)
base.Rank, _ = n.Latest.Lookup(kubernetes.ID)
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.Name)
base.Rank, _ = n.Latest.Lookup(kubernetes.ID)
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

@@ -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,
@@ -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(
Map2Deployment,
ReplicaSetRenderer,
),
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(
Map2ReplicaSet,
PodRenderer,
),
SelectReplicaSet,
),
),
)
// MapContainer2Pod maps container Nodes to pod
// Nodes.
//
@@ -95,40 +123,35 @@ 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 (
Map2Service = Map2Parent(report.Service)
Map2Deployment = Map2Parent(report.Deployment)
Map2ReplicaSet = Map2Parent(report.ReplicaSet)
)
// 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{}
}
// 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 n.Topology == Pseudo {
return report.Nodes{n.ID: n}
}
result := report.Nodes{}
for _, serviceID := range serviceIDs {
serviceName := strings.TrimPrefix(serviceID, namespace+"/")
id := report.MakeServiceNodeID(namespace, serviceName)
node := NewDerivedNode(id, pod).WithTopology(report.Service)
node.Counters = node.Counters.Add(pod.Topology, 1)
result[id] = node
// Otherwise, if some some reason the 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 := n.Parents.Lookup(topology)
if !ok {
return report.Nodes{}
}
result := report.Nodes{}
for _, id := range groupIDs {
node := NewDerivedNode(id, n).WithTopology(topology)
node.Counters = node.Counters.Add(n.Topology, 1)
result[id] = node
}
return result
}
return result
}

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

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

View File

@@ -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,8 +96,10 @@ var (
ServerPodUID = "i9h8g7f6e"
ClientPodNodeID = report.MakePodNodeID(ClientPodUID)
ServerPodNodeID = report.MakePodNodeID(ServerPodUID)
ServiceName = "pongservice"
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))
@@ -367,30 +369,26 @@ 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,
}).
WithSets(report.EmptySets.
Add(kubernetes.ServiceIDs, report.MakeStringSet(ServiceID))).
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{
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,
}).
WithSets(report.EmptySets.
Add(kubernetes.ServiceIDs, report.MakeStringSet(ServiceID))).
WithTopology(report.Pod).WithParents(report.EmptySets.
Add("host", report.MakeStringSet(ServerHostNodeID)).
Add("service", report.MakeStringSet(ServiceID)),
Add("service", report.MakeStringSet(ServiceNodeID)),
),
},
MetadataTemplates: kubernetes.PodMetadataTemplates,
@@ -400,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),
},