Add Kubernetes storage class resource to weave scope

This will:

- Add StorageClass resource. Storage classes are mentioned
in the PVC spec. We're using storage class name from PVC spec to
add adjacency to the PVC node.
- Add square sheet shape for StorageClass.
- Add storage filter in the PODS topology.
Storage Filter will allow user to see distinct view of
stateful applications.
- Add visually distinct edge to show storage adjacency.

Signed-off-by: Satyam Zode <satyam.zode@openebs.io>
This commit is contained in:
Satyam Zode
2018-05-03 15:29:11 +05:30
parent 2f69973de6
commit d26b2c3805
22 changed files with 271 additions and 98 deletions

View File

@@ -48,6 +48,14 @@ var (
{Value: "hide", Label: "Hide Unmanaged", filter: render.IsNotPseudo, filterPseudo: true},
},
}
storageFilter = APITopologyOptionGroup{
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},
},
}
)
// namespaceFilters generates a namespace selector option group based on the given namespaces
@@ -230,7 +238,7 @@ func MakeRegistry() *Registry {
renderer: render.PodRenderer,
Name: "Pods",
Rank: 3,
Options: []APITopologyOptionGroup{unmanagedFilter},
Options: []APITopologyOptionGroup{storageFilter, unmanagedFilter},
HideIfEmpty: true,
},
APITopologyDesc{
@@ -249,14 +257,6 @@ 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,

View File

@@ -5,6 +5,20 @@ import classNames from 'classnames';
import { enterEdge, leaveEdge } from '../actions/app-actions';
import { encodeIdAttribute, decodeIdAttribute } from '../utils/dom-utils';
function getAdjacencyClass(id) {
const topologyId = id.split('---');
const from = topologyId[0].split(';');
const to = topologyId[1].split(';');
if (from[1] !== undefined && to[1] !== undefined) {
from[1] = from[1].slice(1, -1);
to[1] = to[1].slice(1, -1);
if ((from[1] === 'persistent_volume' || from[1] === 'storage_class' || from[1] === 'persistent_volume_claim') || (to[1] === 'persistent_volume' || to[1] === 'storage_class' || to[1] === 'persistent_volume_claim')) {
return 'link-storage';
}
}
return 'link-none';
}
class Edge extends React.Component {
constructor(props, context) {
super(props, context);
@@ -18,7 +32,6 @@ class Edge extends React.Component {
} = this.props;
const shouldRenderMarker = (focused || highlighted) && (source !== target);
const className = classNames('edge', { highlighted });
return (
<g
id={encodeIdAttribute(id)}
@@ -27,6 +40,11 @@ class Edge extends React.Component {
onMouseLeave={this.handleMouseLeave}
>
<path className="shadow" d={path} style={{ strokeWidth: 10 * thickness }} />
<path
className={getAdjacencyClass(id)}
d={path}
style={{ strokeWidth: 5 }}
/>
<path
className="link"
d={path}

View File

@@ -20,7 +20,8 @@ import {
octagonShapeProps,
cloudShapeProps,
cylinderShapeProps,
dottedCylinderShapeProps
dottedCylinderShapeProps,
sheetShapeProps
} from '../utils/node-shape-utils';
import { encodeIdAttribute } from '../utils/dom-utils';
@@ -87,3 +88,4 @@ export const NodeShapeOctagon = props => NodeShape('octagon', pathElement, octag
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);
export const NodeShapeSheet = props => NodeShape('sheet', pathElement, sheetShapeProps, props);

View File

@@ -23,7 +23,8 @@ import {
NodeShapeOctagon,
NodeShapeCloud,
NodeShapeCylinder,
NodeShapeDottedCylinder
NodeShapeDottedCylinder,
NodeShapeSheet
} from './node-shapes';
@@ -38,7 +39,8 @@ const nodeShapes = {
octagon: NodeShapeOctagon,
cloud: NodeShapeCloud,
cylinder: NodeShapeCylinder,
dottedcylinder: NodeShapeDottedCylinder
dottedcylinder: NodeShapeDottedCylinder,
storagesheet: NodeShapeSheet
};
function stackedShape(Shape) {

View File

@@ -32,6 +32,9 @@ export const UNIT_CYLINDER_PATH = 'm -1 -1.25' // this line is responsible for a
+ 'v -1.8'
+ 'a 1 0.4 0 0 0 -2 0';
// Node Storage Sheet Shape
export const SHEET = 'm -1.2 -1.6 m 0.4 0 v 2.4 m -0.4 -2.4 v 2.4 h 2 v -2.4 z m 0 0.4 h 2';
// 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:

View File

@@ -2,7 +2,7 @@ import React from 'react';
import range from 'lodash/range';
import { line, curveCardinalClosed } from 'd3-shape';
import { UNIT_CLOUD_PATH, UNIT_CYLINDER_PATH } from '../constants/styles';
import { UNIT_CLOUD_PATH, UNIT_CYLINDER_PATH, SHEET } from '../constants/styles';
export const pathElement = React.createFactory('path');
@@ -31,3 +31,4 @@ 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 };
export const sheetShapeProps = { d: SHEET };

View File

@@ -533,9 +533,19 @@ a {
}
.edge {
.link {
.link-none {
fill: none;
display: none;
}
.link-storage {
fill: none;
stroke: $edge-color;
stroke-dasharray: 1, 30;
stroke-linecap: round;
}
.link {
fill: none;
stroke: $edge-color
}
.shadow {
fill: none;

View File

@@ -15,6 +15,7 @@ import (
apibatchv2alpha1 "k8s.io/api/batch/v2alpha1"
apiv1 "k8s.io/api/core/v1"
apiextensionsv1beta1 "k8s.io/api/extensions/v1beta1"
storagev1 "k8s.io/api/storage/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
@@ -39,6 +40,7 @@ type Client interface {
WalkNamespaces(f func(NamespaceResource) error) error
WalkPersistentVolumes(f func(PersistentVolume) error) error
WalkPersistentVolumeClaims(f func(PersistentVolumeClaim) error) error
WalkStorageClasses(f func(StorageClass) error) error
WatchPods(f func(Event, Pod))
@@ -62,6 +64,7 @@ type client struct {
namespaceStore cache.Store
persistentVolumeStore cache.Store
persistentVolumeClaimStore cache.Store
storageClassStore cache.Store
podWatchesMutex sync.Mutex
podWatches []func(Event, Pod)
@@ -148,6 +151,7 @@ func NewClient(config ClientConfig) (Client, error) {
result.cronJobStore = result.setupStore("cronjobs")
result.persistentVolumeStore = result.setupStore("persistentvolumes")
result.persistentVolumeClaimStore = result.setupStore("persistentvolumeclaims")
result.storageClassStore = result.setupStore("storageclasses")
return result, nil
}
@@ -190,6 +194,8 @@ func (c *client) clientAndType(resource string) (rest.Interface, interface{}, er
return c.client.CoreV1().RESTClient(), &apiv1.PersistentVolume{}, nil
case "persistentvolumeclaims":
return c.client.CoreV1().RESTClient(), &apiv1.PersistentVolumeClaim{}, nil
case "storageclasses":
return c.client.StorageV1().RESTClient(), &storagev1.StorageClass{}, nil
case "deployments":
return c.client.ExtensionsV1beta1().RESTClient(), &apiextensionsv1beta1.Deployment{}, nil
case "daemonsets":
@@ -292,6 +298,16 @@ func (c *client) WalkPersistentVolumeClaims(f func(PersistentVolumeClaim) error)
return nil
}
func (c *client) WalkStorageClasses(f func(StorageClass) error) error {
for _, m := range c.storageClassStore.List() {
sc := m.(*storagev1.StorageClass)
if err := f(NewStorageClass(sc)); 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)

View File

@@ -32,6 +32,7 @@ const (
Status = report.KubernetesStatus
Message = report.KubernetesMessage
VolumeName = report.KubernetesVolumeName
Provisioner = report.KubernetesProvisioner
)
// Exposed for testing
@@ -123,6 +124,12 @@ var (
AccessModes: {ID: AccessModes, Label: "Access Modes", From: report.FromLatest, Priority: 5},
}
StorageClassMetadataTemplates = report.MetadataTemplates{
NodeType: {ID: NodeType, Label: "Type", From: report.FromLatest, Priority: 1},
Name: {ID: Name, Label: "Name", From: report.FromLatest, Priority: 2},
Provisioner: {ID: Provisioner, Label: "Provisioner", From: report.FromLatest, Priority: 3},
}
TableTemplates = report.TableTemplates{
LabelPrefix: {
ID: LabelPrefix,
@@ -292,6 +299,10 @@ func (r *Reporter) Report() (report.Report, error) {
if err != nil {
return result, err
}
storageClassTopology, _, err := r.storageClassTopology()
if err != nil {
return result, err
}
result.Pod = result.Pod.Merge(podTopology)
result.Service = result.Service.Merge(serviceTopology)
result.Host = result.Host.Merge(hostTopology)
@@ -302,6 +313,7 @@ func (r *Reporter) Report() (report.Report, error) {
result.Namespace = result.Namespace.Merge(namespaceTopology)
result.PersistentVolume = result.PersistentVolume.Merge(persistentVolumeTopology)
result.PersistentVolumeClaim = result.PersistentVolumeClaim.Merge(persistentVolumeClaimTopology)
result.StorageClass = result.StorageClass.Merge(storageClassTopology)
return result, nil
}
@@ -437,6 +449,19 @@ func (r *Reporter) persistentVolumeClaimTopology() (report.Topology, []Persisten
return result, persistentVolumeClaims, err
}
func (r *Reporter) storageClassTopology() (report.Topology, []StorageClass, error) {
storageClasses := []StorageClass{}
result := report.MakeTopology().
WithMetadataTemplates(StorageClassMetadataTemplates).
WithTableTemplates(TableTemplates)
err := r.client.WalkStorageClasses(func(p StorageClass) error {
result.AddNode(p.GetNode(r.probeID))
storageClasses = append(storageClasses, p)
return nil
})
return result, storageClasses, err
}
type labelledChild interface {
Labels() map[string]string
AddParent(string, string)

View File

@@ -154,6 +154,9 @@ func (c *mockClient) WalkPersistentVolumes(f func(kubernetes.PersistentVolume) e
func (c *mockClient) WalkPersistentVolumeClaims(f func(kubernetes.PersistentVolumeClaim) error) error {
return nil
}
func (c *mockClient) WalkStorageClasses(f func(kubernetes.StorageClass) 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]

View File

@@ -0,0 +1,33 @@
package kubernetes
import (
"github.com/weaveworks/scope/report"
storagev1 "k8s.io/api/storage/v1"
)
// StorageClass represent kubernetes StorageClass interface
type StorageClass interface {
Meta
GetNode(probeID string) report.Node
}
// storageClass represents kubernetes storage classes
type storageClass struct {
*storagev1.StorageClass
Meta
}
// NewStorageClass returns new Storage Class type
func NewStorageClass(p *storagev1.StorageClass) StorageClass {
return &storageClass{StorageClass: p, Meta: meta{p.ObjectMeta}}
}
// GetNode returns StorageClass as Node
func (p *storageClass) GetNode(probeID string) 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,
})
}

View File

@@ -199,6 +199,13 @@ var nodeSummaryGroupSpecs = []struct {
Columns: []Column{},
},
},
{
topologyID: report.StorageClass,
NodeSummaryGroup: NodeSummaryGroup{
Label: "Storage Classes",
Columns: []Column{},
},
},
}
func children(rc RenderContext, n report.Node) []NodeSummaryGroup {

View File

@@ -82,6 +82,7 @@ var renderers = map[string]func(BasicNodeSummary, report.Node) BasicNodeSummary{
report.Endpoint: nil, // Do not render
report.PersistentVolume: persistentVolumeNodeSummary,
report.PersistentVolumeClaim: persistentVolumeClaimNodeSummary,
report.StorageClass: storageClassNodeSummary,
}
// For each report.Topology, map to a 'primary' API topology. This can then be used in a variety of places.
@@ -99,8 +100,9 @@ var primaryAPITopology = map[string]string{
report.ECSService: "ecs-services",
report.SwarmService: "swarm-services",
report.Host: "hosts",
report.PersistentVolume: "persistent-volumes",
report.PersistentVolumeClaim: "persistent-volumes",
report.PersistentVolume: "pods",
report.PersistentVolumeClaim: "pods",
report.StorageClass: "pods",
}
// MakeBasicNodeSummary returns a basic summary of a node, if
@@ -385,6 +387,11 @@ func persistentVolumeClaimNodeSummary(base BasicNodeSummary, n report.Node) Basi
return base
}
func storageClassNodeSummary(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 {

View File

@@ -329,6 +329,14 @@ var (
}
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",

View File

@@ -131,6 +131,35 @@ 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 {
var storageComponent bool
if node.Topology == "persistent_volume" || node.Topology == "persistent_volume_claim" || node.Topology == "storage_class" {
storageComponent = true
}
if node.Topology == "pod" {
volumeClaim, ok := node.Latest.Lookup(kubernetes.VolumeClaim)
if !ok {
storageComponent = false
} else if volumeClaim == "" {
storageComponent = false
} else {
storageComponent = true
}
}
return storageComponent
}
// IsNotStorageComponent check whether given node is PV, PVC, SC or not
func IsNotStorageComponent(node report.Node) bool {
var ok bool
ok = true
if node.Topology == "persistent_volume" || node.Topology == "persistent_volume_claim" || node.Topology == "storage_class" {
ok = false
}
return ok
}
// connected returns the node ids of nodes which have edges to/from
// them, excluding edges to/from themselves.
func connected(nodes report.Nodes) map[string]struct{} {

View File

@@ -5,93 +5,34 @@ import (
"github.com/weaveworks/scope/report"
)
// PersistentVolumeRenderer is the common renderer for all the storage components.
var PersistentVolumeRenderer = Memoise(
MakeReduce(
ConnectionStorageJoin(
Map2PVName,
report.PersistentVolumeClaim,
),
MakeFilter(
func(n report.Node) bool {
claimName, ok := n.Latest.Lookup(kubernetes.VolumeClaim)
if claimName == "" {
return !ok
}
return ok
},
MakeReduce(
PropagateSingleMetrics(report.Container,
MakeMap(
Map3Parent([]string{report.Pod}),
MakeFilter(
ComposeFilterFuncs(
IsRunning,
Complement(isPauseContainer),
),
ContainerWithImageNameRenderer,
),
),
),
ConnectionStorageJoin(
Map2PVCName,
report.Pod,
),
),
),
MapStorageEndpoints(
Map2PVNode,
report.PersistentVolume,
),
),
)
// Map3Parent returns a MapFunc which maps Nodes to some parent grouping.
func Map3Parent(
// The topology IDs to look for parents in
topologies []string,
) MapFunc {
return func(n report.Node) report.Nodes {
result := report.Nodes{}
for _, topology := range topologies {
if groupIDs, ok := n.Parents.Lookup(topology); ok {
for _, id := range groupIDs {
node := NewDerivedNode(id, n).WithTopology(topology)
node.Counters = node.Counters.Add(n.Topology, 1)
result[id] = node
}
}
}
return result
}
}
// ConnectionStorageJoin returns connectionStorageJoin object
func ConnectionStorageJoin(toPV func(report.Node) string, topology string) Renderer {
func ConnectionStorageJoin(toPV func(report.Node) []string, topology string) Renderer {
return connectionStorageJoin{toPV: toPV, topology: topology}
}
// connectionStorageJoin holds the information about mapping of storage components
// along with TopologySelector
type connectionStorageJoin struct {
toPV func(report.Node) string
toPV func(report.Node) []string
topology string
}
func (c connectionStorageJoin) Render(rpt report.Report) Nodes {
inputNodes := TopologySelector(c.topology).Render(rpt).Nodes
var pvcNodes = map[string]string{}
var pvcNodes = map[string][]string{}
for _, n := range inputNodes {
pvName := c.toPV(n)
pvcNodes[pvName] = n.ID
for _, name := range pvName {
pvcNodes[name] = append(pvcNodes[name], n.ID)
}
}
return MapStorageEndpoints(
func(m report.Node) string {
func(m report.Node) []string {
pvName, ok := m.Latest.Lookup(kubernetes.Name)
if !ok {
return ""
return []string{""}
}
id := pvcNodes[pvName]
return id
@@ -99,57 +40,81 @@ func (c connectionStorageJoin) Render(rpt report.Report) Nodes {
}
// Map2PVName accepts PV Node and returns Volume name associated with PV Node.
func Map2PVName(m report.Node) string {
func Map2PVName(m report.Node) []string {
pvName, ok := m.Latest.Lookup(kubernetes.VolumeName)
scName, ok1 := m.Latest.Lookup(kubernetes.StorageClassName)
if !ok {
pvName = ""
}
return pvName
if !ok1 {
scName = ""
}
return []string{pvName, scName}
}
// Map2PVCName returns pvc name
func Map2PVCName(m report.Node) string {
func Map2PVCName(m report.Node) []string {
pvcName, ok := m.Latest.Lookup(kubernetes.VolumeClaim)
if !ok {
pvcName = ""
}
return pvcName
return []string{pvcName}
}
// Map2PVNode returns pv node ID
func Map2PVNode(n report.Node) string {
func Map2PVNode(n report.Node) []string {
if pvNodeID, ok := n.Latest.Lookup(report.MakePersistentVolumeNodeID(n.ID)); ok {
return pvNodeID
return []string{pvNodeID}
}
return ""
return []string{""}
}
type storageEndpointMapFunc func(report.Node) []string
// mapStorageEndpoints is the Renderer for rendering storage components together.
type mapStorageEndpoints struct {
f endpointMapFunc
f storageEndpointMapFunc
topology string
}
// MapStorageEndpoints instantiates mapStorageEndpoints and returns same
func MapStorageEndpoints(f endpointMapFunc, topology string) Renderer {
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 == "persistent_volume_claim" {
if e.topology == report.PersistentVolumeClaim {
endpoints = SelectPersistentVolume.Render(rpt)
endpoints.Merge(SelectStorageClass.Render(rpt))
}
if e.topology == "pod" {
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); id != "" {
ret.addChild(n, id, e.topology)
if id := e.f(n); len(id) > 0 {
for _, nodeID := range id {
if nodeID != "" {
ret.addChild(n, nodeID, e.topology)
}
}
}
}
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 ret.storageResult(endpoints)
}
return ret.storageResult(endpoints)
}

View File

@@ -26,6 +26,9 @@ func renderKubernetesTopologies(rpt report.Report) bool {
&rpt.DaemonSet,
&rpt.StatefulSet,
&rpt.CronJob,
&rpt.PersistentVolume,
&rpt.PersistentVolumeClaim,
&rpt.StorageClass,
}
for _, t := range topologies {
if len(t.Nodes) > 0 {
@@ -62,6 +65,22 @@ 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,
),
),
),
))

View File

@@ -267,6 +267,9 @@ func (ret *joinResults) storageResult(input Nodes) Nodes {
}
// 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}
}

View File

@@ -34,4 +34,5 @@ var (
SelectOverlay = TopologySelector(report.Overlay)
SelectPersistentVolume = TopologySelector(report.PersistentVolume)
SelectPersistentVolumeClaim = TopologySelector(report.PersistentVolumeClaim)
SelectStorageClass = TopologySelector(report.StorageClass)
)

View File

@@ -169,6 +169,12 @@ var (
// ParsePersistentVolumeClaimNodeID parses a Persistent Volume Claim node ID
ParsePersistentVolumeClaimNodeID = parseSingleComponentID("persistent_volume_claim")
// MakeStorageClassNodeID produces a storage class node ID from its composite parts.
MakeStorageClassNodeID = makeSingleComponentID("storage_class")
// ParseStorageClassNodeID parses a storage class node ID
ParseStorageClassNodeID = parseSingleComponentID("storage_class")
)
// makeSingleComponentID makes a single-component node id encoder

View File

@@ -78,6 +78,7 @@ const (
KubernetesStatus = "kubernetes_status"
KubernetesMessage = "kubernetes_message"
KubernetesVolumeName = "kubernetes_volume_name"
KubernetesProvisioner = "kubernetes_provisioner"
// probe/awsecs
ECSCluster = "ecs_cluster"
ECSCreatedAt = "ecs_created_at"
@@ -112,6 +113,7 @@ var commonKeys = map[string]string{
SwarmService: SwarmService,
PersistentVolume: PersistentVolume,
PersistentVolumeClaim: PersistentVolumeClaim,
StorageClass: StorageClass,
HostNodeID: HostNodeID,
ControlProbeID: ControlProbeID,

View File

@@ -31,6 +31,7 @@ const (
SwarmService = "swarm_service"
PersistentVolume = "persistent_volume"
PersistentVolumeClaim = "persistent_volume_claim"
StorageClass = "storage_class"
// Shapes used for different nodes
Circle = "circle"
@@ -43,6 +44,7 @@ const (
Cloud = "cloud"
Cylinder = "cylinder"
DottedCylinder = "dottedcylinder"
StorageSheet = "storagesheet"
// Used when counting the number of containers
ContainersKey = "containers"
@@ -69,6 +71,7 @@ var topologyNames = []string{
SwarmService,
PersistentVolume,
PersistentVolumeClaim,
StorageClass,
}
// Report is the core data type. It's produced by probes, and consumed and
@@ -165,6 +168,10 @@ type Report struct {
// Metadata is limited for now, more to come later.
PersistentVolumeClaim Topology
// Storage Class represent all kubernetes Storage Classes on hosts running probes.
// Metadata is limited for now, more to come later.
StorageClass Topology
DNS DNSRecords
// Sampling data for this report.
@@ -266,6 +273,10 @@ func MakeReport() Report {
WithShape(DottedCylinder).
WithLabel("persistent volume claim", "persistent volume claims"),
StorageClass: MakeTopology().
WithShape(StorageSheet).
WithLabel("storage class", "storage classes"),
DNS: DNSRecords{},
Sampling: Sampling{},
@@ -371,6 +382,8 @@ func (r *Report) topology(name string) *Topology {
return &r.PersistentVolume
case PersistentVolumeClaim:
return &r.PersistentVolumeClaim
case StorageClass:
return &r.StorageClass
}
return nil
}