Simplify volume rendering code in render

- Remove the complex volume rendering code.
- Remove volume specific code from Transform.
- Rename filtering methods
- Remove extra params from PV, PVC nodes to make table view look good.

Signed-off-by: Satyam Zode <satyam.zode@openebs.io>
This commit is contained in:
Satyam Zode
2018-05-03 17:07:51 +05:30
parent d26b2c3805
commit 24741bec2a
13 changed files with 172 additions and 234 deletions

View File

@@ -35,7 +35,6 @@ const (
ecsTasksID = "ecs-tasks"
ecsServicesID = "ecs-services"
swarmServicesID = "swarm-services"
persistentVolumesID = "persistent-volumes"
)
var (
@@ -52,8 +51,8 @@ var (
ID: "storage",
Default: "hide",
Options: []APITopologyOption{
{Value: "show", Label: "Show Storage", filter: render.IsStorageComponent, filterPseudo: false},
{Value: "hide", Label: "Hide Storage", filter: render.IsNotStorageComponent, filterPseudo: false},
{Value: "show", Label: "Show Storage", filter: render.IsVolumesComponent, filterPseudo: false},
{Value: "hide", Label: "Hide Storage", filter: render.IsPodComponent, filterPseudo: false},
},
}
)

View File

@@ -8,7 +8,9 @@ import (
// PersistentVolume represent kubernetes PersistentVolume interface
type PersistentVolume interface {
Meta
GetNode(probeID string) report.Node
GetNode() report.Node
GetAccessMode() string
GetVolume() string
}
// persistentVolume represents kubernetes persistent volume
@@ -22,16 +24,34 @@ func NewPersistentVolume(p *apiv1.PersistentVolume) PersistentVolume {
return &persistentVolume{PersistentVolume: p, Meta: meta{p.ObjectMeta}}
}
// GetAccessMode returns access mode associated with PV
func (p *persistentVolume) GetAccessMode() string {
var accessMode string
if len(p.Spec.AccessModes) > 0 {
// A volume can only be mounted using one access mode at a time,
//even if it supports many.
accessMode = string(p.Spec.AccessModes[0])
}
return accessMode
}
// GetVolume returns volume name
func (p *persistentVolume) GetVolume() string {
var volume string
if p.Spec.ClaimRef != nil {
volume = p.Spec.ClaimRef.Name
}
return volume
}
// GetNode returns Persistent Volume as Node
func (p *persistentVolume) GetNode(probeID string) report.Node {
func (p *persistentVolume) GetNode() report.Node {
return p.MetaNode(report.MakePersistentVolumeNodeID(p.UID())).WithLatests(map[string]string{
report.ControlProbeID: probeID,
NodeType: "Persistent Volume",
VolumeClaim: p.Spec.ClaimRef.Name,
StorageClassName: p.Spec.StorageClassName,
Status: string(p.Status.Phase),
AccessModes: string(p.Spec.AccessModes[0]),
ReclaimPolicy: string(p.Spec.PersistentVolumeReclaimPolicy),
Message: p.Status.Message,
NodeType: "Persistent Volume",
VolumeClaim: p.GetVolume(),
StorageClassName: p.Spec.StorageClassName,
Status: string(p.Status.Phase),
AccessModes: p.GetAccessMode(),
})
}

View File

@@ -17,7 +17,7 @@ const (
type PersistentVolumeClaim interface {
Meta
Selector() (labels.Selector, error)
GetNode(probeID string) report.Node
GetNode() report.Node
GetStorageClass() string
}
@@ -48,14 +48,12 @@ func (p *persistentVolumeClaim) GetStorageClass() string {
}
// GetNode returns Persistent Volume Claim as Node
func (p *persistentVolumeClaim) GetNode(probeID string) report.Node {
func (p *persistentVolumeClaim) GetNode() report.Node {
return p.MetaNode(report.MakePersistentVolumeClaimNodeID(p.UID())).WithLatests(map[string]string{
report.ControlProbeID: probeID,
NodeType: "Persistent Volume Claim",
Status: string(p.Status.Phase),
VolumeName: p.Spec.VolumeName,
AccessModes: string(p.Spec.AccessModes[0]),
StorageClassName: p.GetStorageClass(),
NodeType: "Persistent Volume Claim",
Status: string(p.Status.Phase),
VolumeName: p.Spec.VolumeName,
StorageClassName: p.GetStorageClass(),
})
}

View File

@@ -76,7 +76,7 @@ func (p *pod) RestartCount() uint {
}
func (p *pod) VolumeClaimName() string {
claimName := ""
var claimName string
for _, volume := range p.Spec.Volumes {
if volume.VolumeSource.PersistentVolumeClaim != nil {
claimName = volume.VolumeSource.PersistentVolumeClaim.ClaimName
@@ -92,7 +92,10 @@ func (p *pod) GetNode(probeID string) report.Node {
IP: p.Status.PodIP,
report.ControlProbeID: probeID,
RestartCount: strconv.FormatUint(uint64(p.RestartCount()), 10),
VolumeClaim: p.VolumeClaimName(),
}
if p.VolumeClaimName() != "" {
latests[VolumeClaim] = p.VolumeClaimName()
}
if p.Pod.Spec.HostNetwork {

View File

@@ -429,7 +429,7 @@ func (r *Reporter) persistentVolumeTopology() (report.Topology, []PersistentVolu
WithMetadataTemplates(PersistentVolumeMetadataTemplates).
WithTableTemplates(TableTemplates)
err := r.client.WalkPersistentVolumes(func(p PersistentVolume) error {
result.AddNode(p.GetNode(r.probeID))
result.AddNode(p.GetNode())
persistentVolumes = append(persistentVolumes, p)
return nil
})
@@ -442,7 +442,7 @@ func (r *Reporter) persistentVolumeClaimTopology() (report.Topology, []Persisten
WithMetadataTemplates(PersistentVolumeClaimMetadataTemplates).
WithTableTemplates(TableTemplates)
err := r.client.WalkPersistentVolumeClaims(func(p PersistentVolumeClaim) error {
result.AddNode(p.GetNode(r.probeID))
result.AddNode(p.GetNode())
persistentVolumeClaims = append(persistentVolumeClaims, p)
return nil
})
@@ -455,7 +455,7 @@ func (r *Reporter) storageClassTopology() (report.Topology, []StorageClass, erro
WithMetadataTemplates(StorageClassMetadataTemplates).
WithTableTemplates(TableTemplates)
err := r.client.WalkStorageClasses(func(p StorageClass) error {
result.AddNode(p.GetNode(r.probeID))
result.AddNode(p.GetNode())
storageClasses = append(storageClasses, p)
return nil
})

View File

@@ -8,7 +8,7 @@ import (
// StorageClass represent kubernetes StorageClass interface
type StorageClass interface {
Meta
GetNode(probeID string) report.Node
GetNode() report.Node
}
// storageClass represents kubernetes storage classes
@@ -23,11 +23,10 @@ func NewStorageClass(p *storagev1.StorageClass) StorageClass {
}
// GetNode returns StorageClass as Node
func (p *storageClass) GetNode(probeID string) report.Node {
func (p *storageClass) GetNode() report.Node {
return p.MetaNode(report.MakeStorageClassNodeID(p.UID())).WithLatests(map[string]string{
report.ControlProbeID: probeID,
NodeType: "Storage Class",
Name: p.GetName(),
Provisioner: p.Provisioner,
NodeType: "Storage Class",
Name: p.GetName(),
Provisioner: p.Provisioner,
})
}

View File

@@ -12,12 +12,14 @@ import (
// Exported for testing.
var (
circle = "circle"
square = "square"
heptagon = "heptagon"
hexagon = "hexagon"
cloud = "cloud"
cylinder = "cylinder"
circle = "circle"
square = "square"
heptagon = "heptagon"
hexagon = "hexagon"
cloud = "cloud"
cylinder = "cylinder"
dottedcylinder = "dottedcylinder"
storagesheet = "storagesheet"
// Helper to make a report.node with some common options
node = func(topology string) func(id string, adjacent ...string) report.Node {
@@ -41,6 +43,7 @@ var (
hostNode = node(report.Host)
persistentVolume = node(report.PersistentVolume)
persistentVolumeClaim = node(report.PersistentVolumeClaim)
StorageClass = node(report.StorageClass)
UnknownPseudoNode1ID = render.MakePseudoNodeID(fixture.UnknownClient1IP)
UnknownPseudoNode2ID = render.MakePseudoNodeID(fixture.UnknownClient3IP)
@@ -269,6 +272,32 @@ var (
RenderedContainers[fixture.ServerContainerNodeID],
)),
fixture.PersistentVolumeClaimNodeID: persistentVolumeClaim(fixture.PersistentVolumeClaimNodeID, fixture.PersistentVolumeNodeID).
WithLatests(map[string]string{
kubernetes.Name: "pvc-6124",
kubernetes.Namespace: "ping",
kubernetes.Status: "bound",
kubernetes.VolumeName: "pongvolume",
kubernetes.AccessModes: "ReadWriteOnce",
kubernetes.StorageClassName: "standard",
}).WithChild(report.MakeNode(fixture.PersistentVolumeNodeID).WithTopology(report.PersistentVolume)),
fixture.PersistentVolumeNodeID: persistentVolume(fixture.PersistentVolumeNodeID).
WithLatests(map[string]string{
kubernetes.Name: "pongvolume",
kubernetes.Namespace: "ping",
kubernetes.Status: "bound",
kubernetes.VolumeClaim: "pvc-6124",
kubernetes.AccessModes: "ReadWriteOnce",
kubernetes.StorageClassName: "standard",
}),
fixture.StorageClassNodeID: StorageClass(fixture.StorageClassNodeID, fixture.PersistentVolumeClaimNodeID).
WithLatests(map[string]string{
kubernetes.Name: "standard",
kubernetes.Provisioner: "pong",
}).WithChild(report.MakeNode(fixture.PersistentVolumeClaimNodeID).WithTopology(report.PersistentVolumeClaim)),
UnmanagedServerID: unmanagedServerNode,
render.IncomingInternetID: theIncomingInternetNode(fixture.ServerPodNodeID),
render.OutgoingInternetID: theOutgoingInternetNode,
@@ -327,36 +356,6 @@ var (
render.IncomingInternetID: theIncomingInternetNode(fixture.ServerHostNodeID),
render.OutgoingInternetID: theOutgoingInternetNode,
}
RenderedPersistentVolume = report.Nodes{
fixture.ClientPodNodeID: pod(fixture.ClientPodNodeID, fixture.PersistentVolumeClaimNodeID, fixture.ServerPodNodeID).
WithLatests(map[string]string{
kubernetes.Name: "pong-a",
kubernetes.Namespace: "ping",
kubernetes.State: "running",
kubernetes.VolumeClaim: "pvc-6124",
}).WithChild(report.MakeNode(fixture.PersistentVolumeClaimNodeID).WithTopology(report.Pod)),
fixture.PersistentVolumeClaimNodeID: persistentVolumeClaim(fixture.PersistentVolumeClaimNodeID, fixture.PersistentVolumeNodeID).
WithLatests(map[string]string{
kubernetes.Name: "pvc-6124",
kubernetes.Namespace: "ping",
kubernetes.Status: "bound",
kubernetes.VolumeName: "pongvolume",
kubernetes.AccessModes: "ReadWriteOnce",
kubernetes.StorageClassName: "standard",
}).WithChild(report.MakeNode(fixture.PersistentVolumeNodeID).WithTopology(report.PersistentVolume)),
fixture.PersistentVolumeNodeID: persistentVolume(fixture.PersistentVolumeNodeID).
WithLatests(map[string]string{
kubernetes.Name: "pongvolume",
kubernetes.Namespace: "ping",
kubernetes.Status: "bound",
kubernetes.VolumeClaim: "pvc-6124",
kubernetes.AccessModes: "ReadWriteOnce",
kubernetes.StorageClassName: "standard",
}),
}
)
func newu64(value uint64) *uint64 { return &value }

View File

@@ -80,10 +80,7 @@ func (f FilterFunc) Transform(nodes Nodes) Nodes {
newAdjacency = newAdjacency.Add(dstID)
}
}
claimName, ok := node.Latest.Lookup(kubernetes.VolumeClaim)
if claimName == "" || !ok {
node.Adjacency = newAdjacency
}
node.Adjacency = newAdjacency
output[id] = node
}
@@ -131,8 +128,8 @@ func IsConnected(node report.Node) bool {
return ok
}
// IsStorageComponent check whether given node is PV, PVC, SC or not
func IsStorageComponent(node report.Node) bool {
// IsVolumesComponent check whether given node is PV, PVC, SC or not
func IsVolumesComponent(node report.Node) bool {
var storageComponent bool
if node.Topology == "persistent_volume" || node.Topology == "persistent_volume_claim" || node.Topology == "storage_class" {
storageComponent = true
@@ -150,8 +147,8 @@ func IsStorageComponent(node report.Node) bool {
return storageComponent
}
// IsNotStorageComponent check whether given node is PV, PVC, SC or not
func IsNotStorageComponent(node report.Node) bool {
// IsPodComponent check whether given node is everything but PV, PVC, SC
func IsPodComponent(node report.Node) bool {
var ok bool
ok = true
if node.Topology == "persistent_volume" || node.Topology == "persistent_volume_claim" || node.Topology == "storage_class" {

View File

@@ -5,116 +5,84 @@ import (
"github.com/weaveworks/scope/report"
)
// ConnectionStorageJoin returns connectionStorageJoin object
func ConnectionStorageJoin(toPV func(report.Node) []string, topology string) Renderer {
return connectionStorageJoin{toPV: toPV, topology: topology}
}
// KubernetesVolumesRenderer is a Renderer which combines all Kubernetes
// volumes components such as stateful Pods, Persistent Volume, Persistent Volume Claim, Storage Class.
var KubernetesVolumesRenderer = MakeReduce(
VolumesRenderer,
PodToVolumeRenderer,
PVCToStorageClassRenderer,
)
// connectionStorageJoin holds the information about mapping of storage components
// along with TopologySelector
type connectionStorageJoin struct {
toPV func(report.Node) []string
topology string
}
// VolumesRenderer is a Renderer which produces a renderable kubernetes PV & PVC
// graph by merging the pods graph and the Persistent Volume topology.
var VolumesRenderer = volumesRenderer{}
func (c connectionStorageJoin) Render(rpt report.Report) Nodes {
inputNodes := TopologySelector(c.topology).Render(rpt).Nodes
// volumesRenderer is a Renderer to render PV & PVC nodes.
type volumesRenderer struct{}
var pvcNodes = map[string][]string{}
for _, n := range inputNodes {
pvName := c.toPV(n)
for _, name := range pvName {
pvcNodes[name] = append(pvcNodes[name], n.ID)
}
}
return MapStorageEndpoints(
func(m report.Node) []string {
pvName, ok := m.Latest.Lookup(kubernetes.Name)
if !ok {
return []string{""}
// Render renders PV & PVC nodes along with adjacency
func (v volumesRenderer) Render(rpt report.Report) Nodes {
nodes := make(report.Nodes)
for id, n := range rpt.PersistentVolumeClaim.Nodes {
volume, _ := n.Latest.Lookup(kubernetes.VolumeName)
for pvNodeID, p := range rpt.PersistentVolume.Nodes {
volumeName, _ := p.Latest.Lookup(kubernetes.Name)
if volume == volumeName {
n.Adjacency = n.Adjacency.Add(p.ID)
n.Children = n.Children.Add(p)
}
id := pvcNodes[pvName]
return id
}, c.topology).Render(rpt)
nodes[pvNodeID] = p
}
nodes[id] = n
}
return Nodes{Nodes: nodes}
}
// Map2PVName accepts PV Node and returns Volume name associated with PV Node.
func Map2PVName(m report.Node) []string {
pvName, ok := m.Latest.Lookup(kubernetes.VolumeName)
scName, ok1 := m.Latest.Lookup(kubernetes.StorageClassName)
if !ok {
pvName = ""
}
if !ok1 {
scName = ""
}
return []string{pvName, scName}
}
// PodToVolumeRenderer is a Renderer which produces a renderable kubernetes Pod
// graph by merging the pods graph and the Persistent Volume Claim topology.
// Pods having persistent volumes are rendered.
var PodToVolumeRenderer = podToVolumesRenderer{}
// Map2PVCName returns pvc name
func Map2PVCName(m report.Node) []string {
pvcName, ok := m.Latest.Lookup(kubernetes.VolumeClaim)
if !ok {
pvcName = ""
}
return []string{pvcName}
}
// VolumesRenderer is a Renderer to render Pods & PVCs.
type podToVolumesRenderer struct{}
// Map2PVNode returns pv node ID
func Map2PVNode(n report.Node) []string {
if pvNodeID, ok := n.Latest.Lookup(report.MakePersistentVolumeNodeID(n.ID)); ok {
return []string{pvNodeID}
}
return []string{""}
}
type storageEndpointMapFunc func(report.Node) []string
// mapStorageEndpoints is the Renderer for rendering storage components together.
type mapStorageEndpoints struct {
f storageEndpointMapFunc
topology string
}
// MapStorageEndpoints instantiates mapStorageEndpoints and returns same
func MapStorageEndpoints(f storageEndpointMapFunc, topology string) Renderer {
return mapStorageEndpoints{f: f, topology: topology}
}
func (e mapStorageEndpoints) Render(rpt report.Report) Nodes {
var endpoints Nodes
if e.topology == report.PersistentVolumeClaim {
endpoints = SelectPersistentVolume.Render(rpt)
endpoints.Merge(SelectStorageClass.Render(rpt))
}
if e.topology == report.Pod {
endpoints = SelectPersistentVolumeClaim.Render(rpt)
}
ret := newJoinResults(TopologySelector(e.topology).Render(rpt).Nodes)
for _, n := range endpoints.Nodes {
if id := e.f(n); len(id) > 0 {
for _, nodeID := range id {
if nodeID != "" {
ret.addChild(n, nodeID, e.topology)
}
// Render renders the Pod nodes having volumes adjacency.
func (v podToVolumesRenderer) Render(rpt report.Report) Nodes {
nodes := make(report.Nodes)
for podID, podNode := range rpt.Pod.Nodes {
ClaimName, _ := podNode.Latest.Lookup(kubernetes.VolumeClaim)
for _, pvcNode := range rpt.PersistentVolumeClaim.Nodes {
pvcName, _ := pvcNode.Latest.Lookup(kubernetes.Name)
if pvcName == ClaimName {
podNode.Adjacency = podNode.Adjacency.Add(pvcNode.ID)
podNode.Children = podNode.Children.Add(pvcNode)
}
}
nodes[podID] = podNode
}
if e.topology == report.PersistentVolumeClaim {
ret.storageResult(endpoints)
endpoints = SelectStorageClass.Render(rpt)
for _, n := range endpoints.Nodes {
if id := e.f(n); len(id) > 0 {
for _, nodeID := range id {
if nodeID != "" {
ret.addChild(n, nodeID, e.topology)
}
}
return Nodes{Nodes: nodes}
}
// PVCToStorageClassRenderer is a Renderer which produces a renderable kubernetes PVC
// & Storage class graph.
var PVCToStorageClassRenderer = pvcToStorageClassRenderer{}
// pvcToStorageClassRenderer is a Renderer to render PVC & StorageClass.
type pvcToStorageClassRenderer struct{}
// Render renders the PVC & Storage Class nodes with adjacency.
func (v pvcToStorageClassRenderer) Render(rpt report.Report) Nodes {
nodes := make(report.Nodes)
for scID, scNode := range rpt.StorageClass.Nodes {
storageClass, _ := scNode.Latest.Lookup(kubernetes.Name)
for _, pvcNode := range rpt.PersistentVolumeClaim.Nodes {
storageClassName, _ := pvcNode.Latest.Lookup(kubernetes.StorageClassName)
if storageClassName == storageClass {
scNode.Adjacency = scNode.Adjacency.Add(pvcNode.ID)
scNode.Children = scNode.Children.Add(pvcNode)
}
}
return ret.storageResult(endpoints)
nodes[scID] = scNode
}
return ret.storageResult(endpoints)
return Nodes{Nodes: nodes}
}

View File

@@ -1,20 +0,0 @@
package render_test
import (
"testing"
"github.com/weaveworks/common/test"
"github.com/weaveworks/scope/render"
"github.com/weaveworks/scope/render/expected"
"github.com/weaveworks/scope/test/fixture"
"github.com/weaveworks/scope/test/reflect"
"github.com/weaveworks/scope/test/utils"
)
func TestPersistentVolumeRenderer(t *testing.T) {
have := utils.Prune(render.PersistentVolumeRenderer.Render(fixture.Report).Nodes)
want := utils.Prune(expected.RenderedPersistentVolume)
if !reflect.DeepEqual(want, have) {
t.Error(test.Diff(want, have))
}
}

View File

@@ -65,22 +65,7 @@ var PodRenderer = Memoise(ConditionalRenderer(renderKubernetesTopologies,
),
),
ConnectionJoin(MapPod2IP, report.Pod),
ConnectionStorageJoin(
Map2PVName,
report.PersistentVolumeClaim,
),
ConnectionStorageJoin(
Map2PVCName,
report.Pod,
),
MapStorageEndpoints(
Map2PVNode,
report.PersistentVolume,
),
MapStorageEndpoints(
Map2PVNode,
report.StorageClass,
),
KubernetesVolumesRenderer,
),
),
))

View File

@@ -251,29 +251,6 @@ func (ret *joinResults) rewriteAdjacency(outID string, adjacency report.IDList)
ret.nodes[outID] = out
}
// storageAdjacency sets adjacency for the given node ID
func (ret *joinResults) storageAdjacency(outID string, adjacency string) {
out := ret.nodes[outID]
out.Adjacency = out.Adjacency.Add(adjacency)
ret.nodes[outID] = out
}
// storageResult returns Nodes for after adding adjacencies
func (ret *joinResults) storageResult(input Nodes) Nodes {
for _, n := range input.Nodes {
outID, ok := ret.mapped[n.ID]
if !ok {
continue
}
// Since PV and PVC will have only single adjacency
ret.storageAdjacency(outID, n.ID)
for _, outID := range ret.multi[n.ID] {
ret.storageAdjacency(outID, n.ID)
}
}
return Nodes{Nodes: ret.nodes}
}
// ResetCache blows away the rendered node cache, and known service
// cache.
func ResetCache() {

View File

@@ -105,6 +105,8 @@ var (
PersistentVolumeNodeID = report.MakePersistentVolumeNodeID(PersistentVolumeUID)
PersistentVolumeClaimUID = "pvc1234"
PersistentVolumeClaimNodeID = report.MakePersistentVolumeClaimNodeID(PersistentVolumeClaimUID)
StorageClassUID = "sc1234"
StorageClassNodeID = report.MakeStorageClassNodeID(StorageClassUID)
ClientProcess1CPUMetric = report.MakeSingletonMetric(Now.Add(-1*time.Second), 0.01)
ClientProcess1MemoryMetric = report.MakeSingletonMetric(Now.Add(-2*time.Second), 0.02)
@@ -366,7 +368,7 @@ var (
}).
WithTopology(report.PersistentVolumeClaim),
},
}.WithShape(report.Cylinder).WithLabel("persistent volume claim", "persistent volume claims"),
}.WithShape(report.DottedCylinder).WithLabel("persistent volume claim", "persistent volume claims"),
PersistentVolume: report.Topology{
Nodes: report.Nodes{
PersistentVolumeNodeID: report.MakeNodeWith(
@@ -382,6 +384,17 @@ var (
WithTopology(report.PersistentVolume),
},
}.WithShape(report.Cylinder).WithLabel("persistent volume", "persistent volumes"),
StorageClass: report.Topology{
Nodes: report.Nodes{
StorageClassNodeID: report.MakeNodeWith(
StorageClassNodeID, map[string]string{
kubernetes.Name: "standard",
kubernetes.Provisioner: "pong",
}).
WithTopology(report.StorageClass),
},
}.WithShape(report.StorageSheet).WithLabel("storage class", "storage classes"),
Sampling: report.Sampling{
Count: 1024,
Total: 4096,