Skip to content

Commit

Permalink
List application pods under Persistent Volumes tab
Browse files Browse the repository at this point in the history
* List application pods under Persistent Volumes tab

This commit will add functionality to list all the application pods (having volume claim name) under persistent volumes tab (sub-topology of Pods)

Signed-off-by: Akash4927 <[email protected]>

* Add test case for application pods

Signed-off-by: Akash4927 <[email protected]>
  • Loading branch information
qiell authored and satyamz committed Mar 27, 2018
1 parent 2375698 commit fb51672
Show file tree
Hide file tree
Showing 12 changed files with 160 additions and 0 deletions.
74 changes: 74 additions & 0 deletions probe/kubernetes/applicationpod.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package kubernetes

import (
"strconv"

"github.com/weaveworks/scope/report"

apiv1 "k8s.io/api/core/v1"
)

// ApplicationPod represents a Kubernetes pod
type ApplicationPod interface {
Meta
GetApp(probeID string) report.Node
RestartCount() uint
}

type applicationPod struct {
*apiv1.Pod
Meta
parents report.Sets
Node *apiv1.Node
}

func (p *applicationPod) State() string {
return string(p.Status.Phase)
}

func (p *applicationPod) RestartCount() uint {
count := uint(0)
for _, cs := range p.Status.ContainerStatuses {
count += uint(cs.RestartCount)
}
return count
}

func (p *applicationPod) EmptyMetaNode() report.Node {
return report.MakeNode("")
}

func (p *applicationPod) AddParent(topology, id string) {
p.parents = p.parents.Add(topology, report.MakeStringSet(id))
}

// NewApp creates a new Pod
func NewApp(p *apiv1.Pod) ApplicationPod {
return &applicationPod{
Pod: p,
Meta: meta{p.ObjectMeta},
parents: report.MakeSets(),
}
}

func (p *applicationPod) GetApp(probeID string) report.Node {
for _, v := range p.Spec.Volumes {
if v.VolumeSource.PersistentVolumeClaim != nil {
latests := map[string]string{
State: p.State(),
IP: p.Status.PodIP,
report.ControlProbeID: probeID,
RestartCount: strconv.FormatUint(uint64(p.RestartCount()), 10),
VolumeClaimName: v.VolumeSource.PersistentVolumeClaim.ClaimName,
}

if p.Pod.Spec.HostNetwork {
latests[IsInHostNetwork] = "true"
}

return p.MetaNode(report.MakePodNodeID(p.UID())).WithLatests(latests).
WithParents(p.parents)
}
}
return p.EmptyMetaNode()
}
17 changes: 17 additions & 0 deletions probe/kubernetes/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ type Client interface {
WalkPersistentVolumeClaims(f func(PersistentVolumeClaim) error) error
WalkPersistentVolumes(f func(PersistentVolume) error) error
WalkStorageClasses(f func(StorageClass) error) error
WalkApplicationPods(f func(ApplicationPod) error) error

WatchPods(f func(Event, Pod))

Expand All @@ -65,6 +66,7 @@ type client struct {
persistentVolumeClaimStore cache.Store
persistentVolumeStore cache.Store
storageClassStore cache.Store
applicationPodStore cache.Store

podWatchesMutex sync.Mutex
podWatches []func(Event, Pod)
Expand Down Expand Up @@ -152,6 +154,7 @@ func NewClient(config ClientConfig) (Client, error) {
result.persistentVolumeClaimStore = result.setupStore("persistentvolumeclaims")
result.persistentVolumeStore = result.setupStore("persistentvolumes")
result.storageClassStore = result.setupStore("storageclasses")
result.applicationPodStore = result.setupStore("pods")
return result, nil
}

Expand Down Expand Up @@ -370,6 +373,20 @@ func (c *client) WalkStorageClasses(f func(StorageClass) error) error {
return nil
}

// WalkApplicationPods calls f for each application Pod
func (c *client) WalkApplicationPods(f func(ApplicationPod) error) error {
if c.applicationPodStore == nil {
return nil
}
for _, m := range c.applicationPodStore.List() {
p := m.(*apiv1.Pod)
if err := f(NewApp(p)); err != nil {
return err
}
}
return nil
}

// WalkCronJobs calls f for each cronjob
func (c *client) WalkCronJobs(f func(CronJob) error) error {
if c.cronJobStore == nil {
Expand Down
1 change: 1 addition & 0 deletions probe/kubernetes/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const (
Namespace = report.KubernetesNamespace
Created = report.KubernetesCreated
OpenEBSCtrlLabel = report.KubernetesOpenebsCtrlLabel
VolumeClaimName = report.KubernetesVolumeClaimName
OpenEBSCtrlSvcLabel = report.KubernetesOpenebsCtrlSvcLabel
OpenEBSRepLabel = report.KubernetesOpenebsRepLabel
LabelPrefix = "kubernetes_labels_"
Expand Down
26 changes: 26 additions & 0 deletions probe/kubernetes/reporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,10 @@ func (r *Reporter) Report() (report.Report, error) {
if err != nil {
return result, err
}
applicationPodTopology, _, err := r.applicationPodTopology(r.probeID)
if err != nil {
return result, err
}
result.Pod = result.Pod.Merge(podTopology)
result.Service = result.Service.Merge(serviceTopology)
result.Host = result.Host.Merge(hostTopology)
Expand All @@ -306,6 +310,7 @@ func (r *Reporter) Report() (report.Report, error) {
result.PersistentVolumeClaim = result.PersistentVolumeClaim.Merge(persistentVolumeClaimTopology)
result.PersistentVolume = result.PersistentVolume.Merge(persistentVolumeTopology)
result.StorageClass = result.StorageClass.Merge(storageClassTopology)
result.ApplicationPod = result.ApplicationPod.Merge(applicationPodTopology)

return result, nil
}
Expand Down Expand Up @@ -601,3 +606,24 @@ func (r *Reporter) storageClassTopology(probeID string) (report.Topology, []Stor
})
return result, sc, err
}

func (r *Reporter) applicationPodTopology(probeID string) (report.Topology, []ApplicationPod, error) {
//TODO: Need to improve logic for topology.
var (
result = report.MakeTopology().
WithMetadataTemplates(PodMetadataTemplates).
WithMetricTemplates(PodMetricTemplates).
WithTableTemplates(TableTemplates)
ap = []ApplicationPod{}
)

err := r.client.WalkApplicationPods(func(p ApplicationPod) error {
if p.GetApp(probeID).ID != "" {
result.AddNode(p.GetApp(probeID))
ap = append(ap, p)
return nil
}
return nil
})
return result, ap, err
}
3 changes: 3 additions & 0 deletions probe/kubernetes/reporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ func (c *mockClient) WalkPersistentVolumes(f func(kubernetes.PersistentVolume) e
func (c *mockClient) WalkStorageClasses(f func(kubernetes.StorageClass) error) error {
return nil
}
func (c *mockClient) WalkApplicationPods(f func(kubernetes.ApplicationPod) error) error {
return nil
}
func (*mockClient) WatchPods(func(kubernetes.Event, kubernetes.Pod)) {}
func (c *mockClient) GetLogs(namespaceID, podName string, _ []string) (io.ReadCloser, error) {
r, ok := c.logs[namespaceID+";"+podName]
Expand Down
9 changes: 9 additions & 0 deletions render/detailed/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,15 @@ var nodeSummaryGroupSpecs = []struct {
},
},
},
{
topologyID: report.ApplicationPod,
NodeSummaryGroup: NodeSummaryGroup{
Label: "Pod",
Columns: []Column{
{ID: kubernetes.State, Label: "State"},
},
},
},
}

func children(rc RenderContext, n report.Node) []NodeSummaryGroup {
Expand Down
8 changes: 8 additions & 0 deletions render/detailed/summary.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ var renderers = map[string]func(BasicNodeSummary, report.Node) BasicNodeSummary{
report.PersistentVolume: persistentVolumeNodeSummary,
report.StorageClass: storageClassNodeSummary,
report.Overlay: weaveNodeSummary,
report.ApplicationPod: applicationPodNodeSummary,
report.Endpoint: nil, // Do not render
}

Expand All @@ -103,6 +104,7 @@ var primaryAPITopology = map[string]string{
report.PersistentVolumeClaim: "persistentvolumeclaim",
report.PersistentVolume: "persistentvolume",
report.StorageClass: "storageclass",
report.ApplicationPod: "applicationpod",
}

// MakeBasicNodeSummary returns a basic summary of a node, if
Expand Down Expand Up @@ -395,6 +397,12 @@ func storageClassNodeSummary(base BasicNodeSummary, n report.Node) BasicNodeSumm
return base
}

func applicationPodNodeSummary(base BasicNodeSummary, n report.Node) BasicNodeSummary {
// TODO: Need to improve more and add Minor label
base = addKubernetesLabelAndRank(base, n)
return base
}

// groupNodeSummary renders the summary for a group node. n.Topology is
// expected to be of the form: group:container:hostname
func groupNodeSummary(base BasicNodeSummary, r report.Report, n report.Node) BasicNodeSummary {
Expand Down
1 change: 1 addition & 0 deletions render/pvc.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ var PVCRenderer = MakeReduce(
MapEndpoints(endpoint2PVC, report.PersistentVolumeClaim),
MapEndpoints(endpoint2PV, report.PersistentVolume),
MapEndpoints(endpoint2StorageClass, report.StorageClass),
MapEndpoints(endpoint2PVC, report.ApplicationPod),
)

// endpoint2PVC returns pvc node ID
Expand Down
1 change: 1 addition & 0 deletions render/selectors.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@ var (
SelectPersistentVolumeClaim = TopologySelector(report.PersistentVolumeClaim)
SelectPersistentVolume = TopologySelector(report.PersistentVolume)
SelectStorageClass = TopologySelector(report.StorageClass)
ApplicationPod = TopologySelector(report.ApplicationPod)
)
6 changes: 6 additions & 0 deletions report/id.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@ var (

// ParseStorageClassNodeID parses a storageclass node ID
ParseStorageClassNodeID = parseSingleComponentID("storageclass")

// MakeApplicationPodNodeID produces a application pod node ID from its composite parts.
MakeApplicationPodNodeID = makeSingleComponentID("applicationpod")

// ParseApplicationPodNodeID produces a application pod node ID
ParseApplicationPodNodeID = parseSingleComponentID("applicationpod")
)

// makeSingleComponentID makes a single-component node id encoder
Expand Down
2 changes: 2 additions & 0 deletions report/map_keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ const (
KubernetesOpenebsCtrlLabel = "kubernetes_labels_openebs/controller"
KubernetesOpenebsCtrlSvcLabel = "kubernetes_labels_openebs/controller-service"
KubernetesOpenebsRepLabel = "kubernetes_labels_openebs/replica"
KubernetesVolumeClaimName = "kubernetes_volume_claim_name"
// probe/awsecs
ECSCluster = "ecs_cluster"
ECSCreatedAt = "ecs_created_at"
Expand Down Expand Up @@ -110,6 +111,7 @@ var commonKeys = map[string]string{
PersistentVolumeClaim: PersistentVolumeClaim,
PersistentVolume: PersistentVolume,
StorageClass: StorageClass,
ApplicationPod: ApplicationPod,

HostNodeID: HostNodeID,
ControlProbeID: ControlProbeID,
Expand Down
12 changes: 12 additions & 0 deletions report/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const (
PersistentVolumeClaim = "persistentvolumeclaim"
PersistentVolume = "persistentvolume"
StorageClass = "storageclass"
ApplicationPod = "applicationpod"

// Shapes used for different nodes
Circle = "circle"
Expand Down Expand Up @@ -69,6 +70,7 @@ var topologyNames = []string{
PersistentVolumeClaim,
PersistentVolume,
StorageClass,
ApplicationPod,
}

// Report is the core data type. It's produced by probes, and consumed and
Expand Down Expand Up @@ -140,6 +142,10 @@ type Report struct {
// Metadata includes things like storage class id, storage class name etc. Edges are not present
StorageClass Topology

// ApplicationPod represent all kubernetes application pods on hosts running probes.
// Metadata includes things like pod id, name etc. Edges are not present
ApplicationPod Topology

// ContainerImages nodes represent all Docker containers images on
// hosts running probes. Metadata includes things like image id, name etc.
// Edges are not present.
Expand Down Expand Up @@ -274,6 +280,10 @@ func MakeReport() Report {
WithShape(Triangle).
WithLabel("storageclass", "storageclasses"),

ApplicationPod: MakeTopology().
WithShape(Hexagon).
WithLabel("applicationpod", "applicationpods"),

DNS: DNSRecords{},

Sampling: Sampling{},
Expand Down Expand Up @@ -380,6 +390,8 @@ func (r *Report) topology(name string) *Topology {
return &r.PersistentVolume
case StorageClass:
return &r.StorageClass
case ApplicationPod:
return &r.ApplicationPod
}
return nil
}
Expand Down

0 comments on commit fb51672

Please sign in to comment.