diff --git a/app/api_topologies.go b/app/api_topologies.go index 5618a6d9d..0a0d36b61 100644 --- a/app/api_topologies.go +++ b/app/api_topologies.go @@ -35,6 +35,7 @@ const ( ecsTasksID = "ecs-tasks" ecsServicesID = "ecs-services" swarmServicesID = "swarm-services" + persistentVolumesID = "persistent-volumes" ) var ( @@ -248,6 +249,14 @@ func MakeRegistry() *Registry { Options: []APITopologyOptionGroup{unmanagedFilter}, HideIfEmpty: true, }, + APITopologyDesc{ + id: persistentVolumesID, + parent: podsID, + renderer: render.PersistentVolumeRenderer, + Name: "persistent volumes", + Options: []APITopologyOptionGroup{}, + HideIfEmpty: true, + }, APITopologyDesc{ id: ecsTasksID, renderer: render.ECSTaskRenderer, diff --git a/client/app/scripts/charts/node-shapes.js b/client/app/scripts/charts/node-shapes.js index 2fb22e832..fb2dfb700 100644 --- a/client/app/scripts/charts/node-shapes.js +++ b/client/app/scripts/charts/node-shapes.js @@ -19,6 +19,8 @@ import { heptagonShapeProps, octagonShapeProps, cloudShapeProps, + cylinderShapeProps, + dottedCylinderShapeProps } from '../utils/node-shape-utils'; import { encodeIdAttribute } from '../utils/dom-utils'; @@ -83,3 +85,5 @@ export const NodeShapeHexagon = props => NodeShape('hexagon', pathElement, hexag export const NodeShapeHeptagon = props => NodeShape('heptagon', pathElement, heptagonShapeProps, props); export const NodeShapeOctagon = props => NodeShape('octagon', pathElement, octagonShapeProps, props); export const NodeShapeCloud = props => NodeShape('cloud', pathElement, cloudShapeProps, props); +export const NodeShapeCylinder = props => NodeShape('cylinder', pathElement, cylinderShapeProps, props); +export const NodeShapeDottedCylinder = props => NodeShape('dottedcylinder', pathElement, dottedCylinderShapeProps, props); \ No newline at end of file diff --git a/client/app/scripts/charts/node.js b/client/app/scripts/charts/node.js index 34583ce81..4c18f38e6 100644 --- a/client/app/scripts/charts/node.js +++ b/client/app/scripts/charts/node.js @@ -22,6 +22,8 @@ import { NodeShapeHeptagon, NodeShapeOctagon, NodeShapeCloud, + NodeShapeCylinder, + NodeShapeDottedCylinder } from './node-shapes'; @@ -35,6 +37,8 @@ const nodeShapes = { heptagon: NodeShapeHeptagon, octagon: NodeShapeOctagon, cloud: NodeShapeCloud, + cylinder: NodeShapeCylinder, + dottedcylinder: NodeShapeDottedCylinder }; function stackedShape(Shape) { @@ -182,4 +186,4 @@ function mapStateToProps(state) { export default connect( mapStateToProps, { clickNode, enterNode, leaveNode } -)(Node); +)(Node); \ No newline at end of file diff --git a/client/app/scripts/constants/styles.js b/client/app/scripts/constants/styles.js index 7c1c904d7..b45841f51 100644 --- a/client/app/scripts/constants/styles.js +++ b/client/app/scripts/constants/styles.js @@ -22,6 +22,16 @@ export const UNIT_CLOUD_PATH = 'M-1.25 0.233Q-1.25 0.44-1.104 0.587-0.957 0.733- + '0.003-0.036 0.003-0.056 0-0.276-0.196-0.472-0.195-0.195-0.471-0.195-0.206 0-0.373 0.115-0.167' + ' 0.115-0.244 0.299-0.091-0.081-0.216-0.081-0.138 0-0.236 0.098-0.098 0.098-0.098 0.236 0 0.098' + ' 0.054 0.179-0.168 0.039-0.278 0.175-0.109 0.136-0.109 0.312z'; + +// Node Cylinder shape +export const UNIT_CYLINDER_PATH = 'm -1 -1.25' // this line is responsible for adjusting place of the shape with respect to dot + + 'a 1 0.4 0 0 0 2 0' + + 'm -2 0' + + 'v 1.8' + + 'a 1 0.4 0 0 0 2 0' + + 'v -1.8' + + 'a 1 0.4 0 0 0 -2 0'; + // NOTE: This value represents the node unit radius (in pixels). Since zooming is // controlled at the top level now, this renormalization would be obsolete (i.e. // value 1 could be used instead), if it wasn't for the following factors: @@ -89,4 +99,4 @@ export const NODE_DETAILS_TABLE_XS_LABEL = { count: '#', // TODO: consider changing the name of this field on the BE container: '#', -}; +}; \ No newline at end of file diff --git a/client/app/scripts/utils/node-shape-utils.js b/client/app/scripts/utils/node-shape-utils.js index 77f36e305..ee0f0dd75 100644 --- a/client/app/scripts/utils/node-shape-utils.js +++ b/client/app/scripts/utils/node-shape-utils.js @@ -2,7 +2,7 @@ import React from 'react'; import range from 'lodash/range'; import { line, curveCardinalClosed } from 'd3-shape'; -import { UNIT_CLOUD_PATH } from '../constants/styles'; +import { UNIT_CLOUD_PATH, UNIT_CYLINDER_PATH } from '../constants/styles'; export const pathElement = React.createFactory('path'); @@ -13,7 +13,6 @@ function curvedUnitPolygonPath(n) { const curve = curveCardinalClosed.tension(0.65); const spline = line().curve(curve); const innerAngle = (2 * Math.PI) / n; - return spline(range(0, n).map(k => [ Math.sin(k * innerAngle), -Math.cos(k * innerAngle), @@ -30,3 +29,5 @@ export const hexagonShapeProps = { d: curvedUnitPolygonPath(6) }; export const heptagonShapeProps = { d: curvedUnitPolygonPath(7) }; export const octagonShapeProps = { d: curvedUnitPolygonPath(8) }; export const cloudShapeProps = { d: UNIT_CLOUD_PATH }; +export const cylinderShapeProps = { d: UNIT_CYLINDER_PATH }; +export const dottedCylinderShapeProps = { d: UNIT_CYLINDER_PATH }; \ No newline at end of file diff --git a/client/app/styles/_base.scss b/client/app/styles/_base.scss index 887c86962..35d234b13 100644 --- a/client/app/styles/_base.scss +++ b/client/app/styles/_base.scss @@ -619,6 +619,12 @@ a { } } + .shape-dottedcylinder { + .border{ + stroke-dasharray: 0.07; + } + } + .stack .shape .border { stroke-width: $node-border-stroke-width * 0.8; } diff --git a/probe/kubernetes/client.go b/probe/kubernetes/client.go index 7c869ad12..dcf3b9d79 100644 --- a/probe/kubernetes/client.go +++ b/probe/kubernetes/client.go @@ -37,6 +37,8 @@ type Client interface { WalkStatefulSets(f func(StatefulSet) error) error WalkCronJobs(f func(CronJob) error) error WalkNamespaces(f func(NamespaceResource) error) error + WalkPersistentVolumes(f func(PersistentVolume) error) error + WalkPersistentVolumeClaims(f func(PersistentVolumeClaim) error) error WatchPods(f func(Event, Pod)) @@ -47,17 +49,19 @@ type Client interface { } type client struct { - quit chan struct{} - client *kubernetes.Clientset - podStore cache.Store - serviceStore cache.Store - deploymentStore cache.Store - daemonSetStore cache.Store - statefulSetStore cache.Store - jobStore cache.Store - cronJobStore cache.Store - nodeStore cache.Store - namespaceStore cache.Store + quit chan struct{} + client *kubernetes.Clientset + podStore cache.Store + serviceStore cache.Store + deploymentStore cache.Store + daemonSetStore cache.Store + statefulSetStore cache.Store + jobStore cache.Store + cronJobStore cache.Store + nodeStore cache.Store + namespaceStore cache.Store + persistentVolumeStore cache.Store + persistentVolumeClaimStore cache.Store podWatchesMutex sync.Mutex podWatches []func(Event, Pod) @@ -142,6 +146,8 @@ func NewClient(config ClientConfig) (Client, error) { result.jobStore = result.setupStore("jobs") result.statefulSetStore = result.setupStore("statefulsets") result.cronJobStore = result.setupStore("cronjobs") + result.persistentVolumeStore = result.setupStore("persistentvolumes") + result.persistentVolumeClaimStore = result.setupStore("persistentvolumeclaims") return result, nil } @@ -180,6 +186,10 @@ func (c *client) clientAndType(resource string) (rest.Interface, interface{}, er return c.client.CoreV1().RESTClient(), &apiv1.Node{}, nil case "namespaces": return c.client.CoreV1().RESTClient(), &apiv1.Namespace{}, nil + case "persistentvolumes": + return c.client.CoreV1().RESTClient(), &apiv1.PersistentVolume{}, nil + case "persistentvolumeclaims": + return c.client.CoreV1().RESTClient(), &apiv1.PersistentVolumeClaim{}, nil case "deployments": return c.client.ExtensionsV1beta1().RESTClient(), &apiextensionsv1beta1.Deployment{}, nil case "daemonsets": @@ -262,6 +272,26 @@ func (c *client) WalkPods(f func(Pod) error) error { return nil } +func (c *client) WalkPersistentVolumes(f func(PersistentVolume) error) error { + for _, m := range c.persistentVolumeStore.List() { + pv := m.(*apiv1.PersistentVolume) + if err := f(NewPersistentVolume(pv)); err != nil { + return err + } + } + return nil +} + +func (c *client) WalkPersistentVolumeClaims(f func(PersistentVolumeClaim) error) error { + for _, m := range c.persistentVolumeClaimStore.List() { + pvc := m.(*apiv1.PersistentVolumeClaim) + if err := f(NewPersistentVolumeClaim(pvc)); err != nil { + return err + } + } + return nil +} + func (c *client) WalkServices(f func(Service) error) error { for _, m := range c.serviceStore.List() { s := m.(*apiv1.Service) diff --git a/probe/kubernetes/persistentvolume.go b/probe/kubernetes/persistentvolume.go new file mode 100644 index 000000000..2d870db92 --- /dev/null +++ b/probe/kubernetes/persistentvolume.go @@ -0,0 +1,37 @@ +package kubernetes + +import ( + "github.com/weaveworks/scope/report" + apiv1 "k8s.io/api/core/v1" +) + +// PersistentVolume represent kubernetes PersistentVolume interface +type PersistentVolume interface { + Meta + GetNode(probeID string) report.Node +} + +// persistentVolume represents kubernetes persistent volume +type persistentVolume struct { + *apiv1.PersistentVolume + Meta +} + +// NewPersistentVolume returns new persistentVolume type +func NewPersistentVolume(p *apiv1.PersistentVolume) PersistentVolume { + return &persistentVolume{PersistentVolume: p, Meta: meta{p.ObjectMeta}} +} + +// GetNode returns Persistent Volume as Node +func (p *persistentVolume) GetNode(probeID string) 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, + }) +} diff --git a/probe/kubernetes/persistentvolumeclaim.go b/probe/kubernetes/persistentvolumeclaim.go new file mode 100644 index 000000000..1f0c0121d --- /dev/null +++ b/probe/kubernetes/persistentvolumeclaim.go @@ -0,0 +1,48 @@ +package kubernetes + +import ( + "github.com/weaveworks/scope/report" + apiv1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "k8s.io/apimachinery/pkg/labels" +) + +// PersistentVolumeClaim represents kubernetes PVC interface +type PersistentVolumeClaim interface { + Meta + Selector() (labels.Selector, error) + GetNode(probeID string) report.Node +} + +// persistentVolumeClaim represents kubernetes Persistent Volume Claims +type persistentVolumeClaim struct { + *apiv1.PersistentVolumeClaim + Meta +} + +// NewPersistentVolumeClaim returns new Persistent Volume Claim type +func NewPersistentVolumeClaim(p *apiv1.PersistentVolumeClaim) PersistentVolumeClaim { + return &persistentVolumeClaim{PersistentVolumeClaim: p, Meta: meta{p.ObjectMeta}} +} + +// GetNode returns Persistent Volume Claim as Node +func (p *persistentVolumeClaim) GetNode(probeID string) report.Node { + return p.MetaNode(report.MakePersistentVolumeClaimNodeID(p.UID())).WithLatests(map[string]string{ + report.ControlProbeID: probeID, + NodeType: "Persistent Volume Claim", + Namespace: p.GetNamespace(), + Status: string(p.Status.Phase), + VolumeName: p.Spec.VolumeName, + AccessModes: string(p.Spec.AccessModes[0]), + }) +} + +// Selector returns all Persistent Volume Claim selector +func (p *persistentVolumeClaim) Selector() (labels.Selector, error) { + selector, err := metav1.LabelSelectorAsSelector(p.Spec.Selector) + if err != nil { + return nil, err + } + return selector, nil +} diff --git a/probe/kubernetes/reporter.go b/probe/kubernetes/reporter.go index b46d6a0aa..37cbd914d 100644 --- a/probe/kubernetes/reporter.go +++ b/probe/kubernetes/reporter.go @@ -25,6 +25,13 @@ const ( NodeType = report.KubernetesNodeType Type = report.KubernetesType Ports = report.KubernetesPorts + VolumeClaim = report.KubernetesVolumeClaim + StorageClassName = report.KubernetesStorageClassName + AccessModes = report.KubernetesAccessModes + ReclaimPolicy = report.KubernetesReclaimPolicy + Status = report.KubernetesStatus + Message = report.KubernetesMessage + VolumeName = report.KubernetesVolumeName ) // Exposed for testing @@ -98,6 +105,24 @@ var ( CronJobMetricTemplates = PodMetricTemplates + PersistentVolumeMetadataTemplates = report.MetadataTemplates{ + NodeType: {ID: NodeType, Label: "Type", From: report.FromLatest, Priority: 1}, + VolumeClaim: {ID: VolumeClaim, Label: "Volume Claim", From: report.FromLatest, Priority: 2}, + StorageClassName: {ID: StorageClassName, Label: "Storage Class", From: report.FromLatest, Priority: 3}, + ReclaimPolicy: {ID: ReclaimPolicy, Label: "Reclaim Policy", From: report.FromLatest, Priority: 4}, + AccessModes: {ID: AccessModes, Label: "Access Modes", From: report.FromLatest, Priority: 5}, + Status: {ID: Status, Label: "Status", From: report.FromLatest, Priority: 6}, + Message: {ID: Message, Label: "Message", From: report.FromLatest, Priority: 7}, + } + + PersistentVolumeClaimMetadataTemplates = report.MetadataTemplates{ + NodeType: {ID: NodeType, Label: "Type", From: report.FromLatest, Priority: 1}, + Namespace: {ID: Namespace, Label: "Namespace", From: report.FromLatest, Priority: 2}, + Status: {ID: Status, Label: "Status", From: report.FromLatest, Priority: 3}, + VolumeName: {ID: VolumeName, Label: "Volume", From: report.FromLatest, Priority: 4}, + AccessModes: {ID: AccessModes, Label: "Access Modes", From: report.FromLatest, Priority: 5}, + } + TableTemplates = report.TableTemplates{ LabelPrefix: { ID: LabelPrefix, @@ -259,6 +284,14 @@ func (r *Reporter) Report() (report.Report, error) { if err != nil { return result, err } + persistentVolumeTopology, _, err := r.persistentVolumeTopology() + if err != nil { + return result, err + } + persistentVolumeClaimTopology, _, err := r.persistentVolumeClaimTopology() + if err != nil { + return result, err + } result.Pod = result.Pod.Merge(podTopology) result.Service = result.Service.Merge(serviceTopology) result.Host = result.Host.Merge(hostTopology) @@ -267,6 +300,8 @@ func (r *Reporter) Report() (report.Report, error) { result.CronJob = result.CronJob.Merge(cronJobTopology) result.Deployment = result.Deployment.Merge(deploymentTopology) result.Namespace = result.Namespace.Merge(namespaceTopology) + result.PersistentVolume = result.PersistentVolume.Merge(persistentVolumeTopology) + result.PersistentVolumeClaim = result.PersistentVolumeClaim.Merge(persistentVolumeClaimTopology) return result, nil } @@ -376,6 +411,32 @@ func (r *Reporter) cronJobTopology() (report.Topology, []CronJob, error) { return result, cronJobs, err } +func (r *Reporter) persistentVolumeTopology() (report.Topology, []PersistentVolume, error) { + persistentVolumes := []PersistentVolume{} + result := report.MakeTopology(). + WithMetadataTemplates(PersistentVolumeMetadataTemplates). + WithTableTemplates(TableTemplates) + err := r.client.WalkPersistentVolumes(func(p PersistentVolume) error { + result.AddNode(p.GetNode(r.probeID)) + persistentVolumes = append(persistentVolumes, p) + return nil + }) + return result, persistentVolumes, err +} + +func (r *Reporter) persistentVolumeClaimTopology() (report.Topology, []PersistentVolumeClaim, error) { + persistentVolumeClaims := []PersistentVolumeClaim{} + result := report.MakeTopology(). + WithMetadataTemplates(PersistentVolumeClaimMetadataTemplates). + WithTableTemplates(TableTemplates) + err := r.client.WalkPersistentVolumeClaims(func(p PersistentVolumeClaim) error { + result.AddNode(p.GetNode(r.probeID)) + persistentVolumeClaims = append(persistentVolumeClaims, p) + return nil + }) + return result, persistentVolumeClaims, err +} + type labelledChild interface { Labels() map[string]string AddParent(string, string) diff --git a/probe/kubernetes/reporter_test.go b/probe/kubernetes/reporter_test.go index 6be9dd5ad..e4aa304e8 100644 --- a/probe/kubernetes/reporter_test.go +++ b/probe/kubernetes/reporter_test.go @@ -148,6 +148,12 @@ func (c *mockClient) WalkDeployments(f func(kubernetes.Deployment) error) error func (c *mockClient) WalkNamespaces(f func(kubernetes.NamespaceResource) error) error { return nil } +func (c *mockClient) WalkPersistentVolumes(f func(kubernetes.PersistentVolume) error) error { + return nil +} +func (c *mockClient) WalkPersistentVolumeClaims(f func(kubernetes.PersistentVolumeClaim) 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] diff --git a/render/detailed/node.go b/render/detailed/node.go index e0e972694..5e7c5fc7d 100644 --- a/render/detailed/node.go +++ b/render/detailed/node.go @@ -185,6 +185,20 @@ var nodeSummaryGroupSpecs = []struct { Columns: []Column{}, }, }, + { + topologyID: report.PersistentVolume, + NodeSummaryGroup: NodeSummaryGroup{ + Label: "Persistent Volumes", + Columns: []Column{}, + }, + }, + { + topologyID: report.PersistentVolumeClaim, + NodeSummaryGroup: NodeSummaryGroup{ + Label: "Persistent Volume Claims", + Columns: []Column{}, + }, + }, } func children(rc RenderContext, n report.Node) []NodeSummaryGroup { diff --git a/render/detailed/summary.go b/render/detailed/summary.go index 960e102c5..3613f02b8 100644 --- a/render/detailed/summary.go +++ b/render/detailed/summary.go @@ -64,39 +64,43 @@ type NodeSummary struct { } var renderers = map[string]func(BasicNodeSummary, report.Node) BasicNodeSummary{ - render.Pseudo: pseudoNodeSummary, - report.Process: processNodeSummary, - report.Container: containerNodeSummary, - report.ContainerImage: containerImageNodeSummary, - report.Pod: podNodeSummary, - report.Service: podGroupNodeSummary, - report.Deployment: podGroupNodeSummary, - report.DaemonSet: podGroupNodeSummary, - report.StatefulSet: podGroupNodeSummary, - report.CronJob: podGroupNodeSummary, - report.ECSTask: ecsTaskNodeSummary, - report.ECSService: ecsServiceNodeSummary, - report.SwarmService: swarmServiceNodeSummary, - report.Host: hostNodeSummary, - report.Overlay: weaveNodeSummary, - report.Endpoint: nil, // Do not render + render.Pseudo: pseudoNodeSummary, + report.Process: processNodeSummary, + report.Container: containerNodeSummary, + report.ContainerImage: containerImageNodeSummary, + report.Pod: podNodeSummary, + report.Service: podGroupNodeSummary, + report.Deployment: podGroupNodeSummary, + report.DaemonSet: podGroupNodeSummary, + report.StatefulSet: podGroupNodeSummary, + report.CronJob: podGroupNodeSummary, + report.ECSTask: ecsTaskNodeSummary, + report.ECSService: ecsServiceNodeSummary, + report.SwarmService: swarmServiceNodeSummary, + report.Host: hostNodeSummary, + report.Overlay: weaveNodeSummary, + report.Endpoint: nil, // Do not render + report.PersistentVolume: persistentVolumeNodeSummary, + report.PersistentVolumeClaim: persistentVolumeClaimNodeSummary, } // For each report.Topology, map to a 'primary' API topology. This can then be used in a variety of places. var primaryAPITopology = map[string]string{ - report.Process: "processes", - report.Container: "containers", - report.ContainerImage: "containers-by-image", - report.Pod: "pods", - report.Deployment: "kube-controllers", - report.DaemonSet: "kube-controllers", - report.StatefulSet: "kube-controllers", - report.CronJob: "kube-controllers", - report.Service: "services", - report.ECSTask: "ecs-tasks", - report.ECSService: "ecs-services", - report.SwarmService: "swarm-services", - report.Host: "hosts", + report.Process: "processes", + report.Container: "containers", + report.ContainerImage: "containers-by-image", + report.Pod: "pods", + report.Deployment: "kube-controllers", + report.DaemonSet: "kube-controllers", + report.StatefulSet: "kube-controllers", + report.CronJob: "kube-controllers", + report.Service: "services", + report.ECSTask: "ecs-tasks", + report.ECSService: "ecs-services", + report.SwarmService: "swarm-services", + report.Host: "hosts", + report.PersistentVolume: "persistent-volumes", + report.PersistentVolumeClaim: "persistent-volumes", } // MakeBasicNodeSummary returns a basic summary of a node, if @@ -371,6 +375,16 @@ func weaveNodeSummary(base BasicNodeSummary, n report.Node) BasicNodeSummary { return base } +func persistentVolumeNodeSummary(base BasicNodeSummary, n report.Node) BasicNodeSummary { + base = addKubernetesLabelAndRank(base, n) + return base +} + +func persistentVolumeClaimNodeSummary(base BasicNodeSummary, n report.Node) BasicNodeSummary { + 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 { diff --git a/render/selectors.go b/render/selectors.go index 6b80e0ac4..18fcfc20f 100644 --- a/render/selectors.go +++ b/render/selectors.go @@ -17,19 +17,21 @@ func (t TopologySelector) Render(r report.Report) Nodes { // The topology selectors implement a Renderer which fetch the nodes from the // various report topologies. var ( - SelectEndpoint = TopologySelector(report.Endpoint) - SelectProcess = TopologySelector(report.Process) - SelectContainer = TopologySelector(report.Container) - SelectContainerImage = TopologySelector(report.ContainerImage) - SelectHost = TopologySelector(report.Host) - SelectPod = TopologySelector(report.Pod) - SelectService = TopologySelector(report.Service) - SelectDeployment = TopologySelector(report.Deployment) - SelectDaemonSet = TopologySelector(report.DaemonSet) - SelectStatefulSet = TopologySelector(report.StatefulSet) - SelectCronJob = TopologySelector(report.CronJob) - SelectECSTask = TopologySelector(report.ECSTask) - SelectECSService = TopologySelector(report.ECSService) - SelectSwarmService = TopologySelector(report.SwarmService) - SelectOverlay = TopologySelector(report.Overlay) + SelectEndpoint = TopologySelector(report.Endpoint) + SelectProcess = TopologySelector(report.Process) + SelectContainer = TopologySelector(report.Container) + SelectContainerImage = TopologySelector(report.ContainerImage) + SelectHost = TopologySelector(report.Host) + SelectPod = TopologySelector(report.Pod) + SelectService = TopologySelector(report.Service) + SelectDeployment = TopologySelector(report.Deployment) + SelectDaemonSet = TopologySelector(report.DaemonSet) + SelectStatefulSet = TopologySelector(report.StatefulSet) + SelectCronJob = TopologySelector(report.CronJob) + SelectECSTask = TopologySelector(report.ECSTask) + SelectECSService = TopologySelector(report.ECSService) + SelectSwarmService = TopologySelector(report.SwarmService) + SelectOverlay = TopologySelector(report.Overlay) + SelectPersistentVolume = TopologySelector(report.PersistentVolume) + SelectPersistentVolumeClaim = TopologySelector(report.PersistentVolumeClaim) ) diff --git a/report/id.go b/report/id.go index 3b23df2f5..44a265022 100644 --- a/report/id.go +++ b/report/id.go @@ -157,6 +157,18 @@ var ( // ParseSwarmServiceNodeID parses a Swarm service node ID ParseSwarmServiceNodeID = parseSingleComponentID("swarm_service") + + // MakePersistentVolumeNodeID produces a Persistent Volume node ID from its composite parts. + MakePersistentVolumeNodeID = makeSingleComponentID("persistent_volume") + + // ParsePersistentVolumeNodeID parses a Persistent Volume node ID + ParsePersistentVolumeNodeID = parseSingleComponentID("persistent_volume") + + // MakePersistentVolumeClaimNodeID produces a Persistent Volume Claim node ID from its composite parts. + MakePersistentVolumeClaimNodeID = makeSingleComponentID("persistent_volume_claim") + + // ParsePersistentVolumeClaimNodeID parses a Persistent Volume Claim node ID + ParsePersistentVolumeClaimNodeID = parseSingleComponentID("persistent_volume_claim") ) // makeSingleComponentID makes a single-component node id encoder diff --git a/report/map_keys.go b/report/map_keys.go index 8cfaf3563..783386dd5 100644 --- a/report/map_keys.go +++ b/report/map_keys.go @@ -71,6 +71,13 @@ const ( KubernetesActiveJobs = "kubernetes_active_jobs" KubernetesType = "kubernetes_type" KubernetesPorts = "kubernetes_ports" + KubernetesVolumeClaim = "kubernetes_volume_claim" + KubernetesStorageClassName = "kubernetes_storage_class_name" + KubernetesAccessModes = "kubernetes_access_modes" + KubernetesReclaimPolicy = "kubernetes_reclaim_policy" + KubernetesStatus = "kubernetes_status" + KubernetesMessage = "kubernetes_message" + KubernetesVolumeName = "kubernetes_volume_name" // probe/awsecs ECSCluster = "ecs_cluster" ECSCreatedAt = "ecs_created_at" @@ -87,22 +94,24 @@ const ( getting clogged with values that are only used once. */ var commonKeys = map[string]string{ - Endpoint: Endpoint, - Process: Process, - Container: Container, - Pod: Pod, - Service: Service, - Deployment: Deployment, - ReplicaSet: ReplicaSet, - DaemonSet: DaemonSet, - StatefulSet: StatefulSet, - CronJob: CronJob, - ContainerImage: ContainerImage, - Host: Host, - Overlay: Overlay, - ECSService: ECSService, - ECSTask: ECSTask, - SwarmService: SwarmService, + Endpoint: Endpoint, + Process: Process, + Container: Container, + Pod: Pod, + Service: Service, + Deployment: Deployment, + ReplicaSet: ReplicaSet, + DaemonSet: DaemonSet, + StatefulSet: StatefulSet, + CronJob: CronJob, + ContainerImage: ContainerImage, + Host: Host, + Overlay: Overlay, + ECSService: ECSService, + ECSTask: ECSTask, + SwarmService: SwarmService, + PersistentVolume: PersistentVolume, + PersistentVolumeClaim: PersistentVolumeClaim, HostNodeID: HostNodeID, ControlProbeID: ControlProbeID, diff --git a/report/report.go b/report/report.go index 3542f3b18..a6eca7add 100644 --- a/report/report.go +++ b/report/report.go @@ -12,33 +12,37 @@ import ( // Names of the various topologies. const ( - Endpoint = "endpoint" - Process = "process" - Container = "container" - Pod = "pod" - Service = "service" - Deployment = "deployment" - ReplicaSet = "replica_set" - DaemonSet = "daemon_set" - StatefulSet = "stateful_set" - CronJob = "cron_job" - Namespace = "namespace" - ContainerImage = "container_image" - Host = "host" - Overlay = "overlay" - ECSService = "ecs_service" - ECSTask = "ecs_task" - SwarmService = "swarm_service" + Endpoint = "endpoint" + Process = "process" + Container = "container" + Pod = "pod" + Service = "service" + Deployment = "deployment" + ReplicaSet = "replica_set" + DaemonSet = "daemon_set" + StatefulSet = "stateful_set" + CronJob = "cron_job" + Namespace = "namespace" + ContainerImage = "container_image" + Host = "host" + Overlay = "overlay" + ECSService = "ecs_service" + ECSTask = "ecs_task" + SwarmService = "swarm_service" + PersistentVolume = "persistent_volume" + PersistentVolumeClaim = "persistent_volume_claim" // Shapes used for different nodes - Circle = "circle" - Triangle = "triangle" - Square = "square" - Pentagon = "pentagon" - Hexagon = "hexagon" - Heptagon = "heptagon" - Octagon = "octagon" - Cloud = "cloud" + Circle = "circle" + Triangle = "triangle" + Square = "square" + Pentagon = "pentagon" + Hexagon = "hexagon" + Heptagon = "heptagon" + Octagon = "octagon" + Cloud = "cloud" + Cylinder = "cylinder" + DottedCylinder = "dottedcylinder" // Used when counting the number of containers ContainersKey = "containers" @@ -63,6 +67,8 @@ var topologyNames = []string{ ECSTask, ECSService, SwarmService, + PersistentVolume, + PersistentVolumeClaim, } // Report is the core data type. It's produced by probes, and consumed and @@ -151,6 +157,14 @@ type Report struct { // their status endpoints. Edges are present. Overlay Topology + // Persistent Volume nodes represent all Kubernetes Persistent Volumes running on hosts running probes. + // Metadata is limited for now, more to come later. + PersistentVolume Topology + + // Persistent Volume Claim nodes represent all Kubernetes Persistent Volume Claims running on hosts running probes. + // Metadata is limited for now, more to come later. + PersistentVolumeClaim Topology + DNS DNSRecords // Sampling data for this report. @@ -244,6 +258,14 @@ func MakeReport() Report { WithShape(Heptagon). WithLabel("service", "services"), + PersistentVolume: MakeTopology(). + WithShape(Cylinder). + WithLabel("persistent volume", "persistent volumes"), + + PersistentVolumeClaim: MakeTopology(). + WithShape(DottedCylinder). + WithLabel("persistent volume claim", "persistent volume claims"), + DNS: DNSRecords{}, Sampling: Sampling{}, @@ -345,6 +367,10 @@ func (r *Report) topology(name string) *Topology { return &r.ECSService case SwarmService: return &r.SwarmService + case PersistentVolume: + return &r.PersistentVolume + case PersistentVolumeClaim: + return &r.PersistentVolumeClaim } return nil }