mirror of
https://github.com/weaveworks/scope.git
synced 2026-07-18 04:49:55 +00:00
Add describe control to all k8s resources
This commit will add a new control i.e. Describe which will describe a k8s resource similar to kubectl describe. Signed-off-by: Akash Srivastava <akashsrivastava4927@gmail.com>
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
package kubernetes
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -14,6 +16,7 @@ import (
|
||||
snapshot "github.com/openebs/k8s-snapshot-client/snapshot/pkg/client/clientset/versioned"
|
||||
"github.com/pborman/uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
apiappsv1 "k8s.io/api/apps/v1"
|
||||
apiappsv1beta1 "k8s.io/api/apps/v1beta1"
|
||||
apibatchv1 "k8s.io/api/batch/v1"
|
||||
apibatchv1beta1 "k8s.io/api/batch/v1beta1"
|
||||
@@ -22,6 +25,7 @@ import (
|
||||
apiextensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||
storagev1 "k8s.io/api/storage/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
@@ -32,6 +36,8 @@ import (
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
kubectldescribe "k8s.io/kubernetes/pkg/kubectl/describe"
|
||||
kubectl "k8s.io/kubernetes/pkg/kubectl/describe/versioned"
|
||||
)
|
||||
|
||||
// Client keeps track of running kubernetes pods and services
|
||||
@@ -55,12 +61,28 @@ type Client interface {
|
||||
CloneVolumeSnapshot(namespaceID, volumeSnapshotID, persistentVolumeClaimID, capacity string) error
|
||||
CreateVolumeSnapshot(namespaceID, persistentVolumeClaimID, capacity string) error
|
||||
GetLogs(namespaceID, podID string, containerNames []string) (io.ReadCloser, error)
|
||||
Describe(namespaceID, resourceID string, groupKind schema.GroupKind, restMapping apimeta.RESTMapping) (io.ReadCloser, error)
|
||||
DeletePod(namespaceID, podID string) error
|
||||
DeleteVolumeSnapshot(namespaceID, volumeSnapshotID string) error
|
||||
ScaleUp(namespaceID, id string) error
|
||||
ScaleDown(namespaceID, id string) error
|
||||
}
|
||||
|
||||
// ResourceMap is the mapping of resource and their GroupKind
|
||||
var ResourceMap = map[string]schema.GroupKind{
|
||||
"Pod": {Group: apiv1.GroupName, Kind: "Pod"},
|
||||
"Service": {Group: apiv1.GroupName, Kind: "Service"},
|
||||
"Deployment": {Group: apiappsv1.GroupName, Kind: "Deployment"},
|
||||
"DaemonSet": {Group: apiappsv1.GroupName, Kind: "DaemonSet"},
|
||||
"StatefulSet": {Group: apiappsv1.GroupName, Kind: "StatefulSet"},
|
||||
"Job": {Group: apibatchv1.GroupName, Kind: "Job"},
|
||||
"CronJob": {Group: apibatchv1.GroupName, Kind: "CronJob"},
|
||||
"Node": {Group: apiv1.GroupName, Kind: "Node"},
|
||||
"PersistentVolume": {Group: apiv1.GroupName, Kind: "PersistentVolume"},
|
||||
"PersistentVolumeClaim": {Group: apiv1.GroupName, Kind: "PersistentVolumeClaim"},
|
||||
"StorageClass": {Group: storagev1.GroupName, Kind: "StorageClass"},
|
||||
}
|
||||
|
||||
type client struct {
|
||||
quit chan struct{}
|
||||
client *kubernetes.Clientset
|
||||
@@ -541,6 +563,32 @@ func (c *client) GetLogs(namespaceID, podID string, containerNames []string) (io
|
||||
return NewLogReadCloser(readClosersWithLabel), nil
|
||||
}
|
||||
|
||||
func (c *client) Describe(namespaceID, resourceID string, groupKind schema.GroupKind, restMapping apimeta.RESTMapping) (io.ReadCloser, error) {
|
||||
readClosersWithLabel := map[io.ReadCloser]string{}
|
||||
restConfig, err := rest.InClusterConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
describer, ok := kubectl.DescriberFor(groupKind, restConfig)
|
||||
if !ok {
|
||||
describer, ok = kubectl.GenericDescriberFor(&restMapping, restConfig)
|
||||
if !ok {
|
||||
return nil, errors.New("Resource not found")
|
||||
}
|
||||
}
|
||||
describerSetting := kubectldescribe.DescriberSettings{
|
||||
ShowEvents: true,
|
||||
}
|
||||
obj, err := describer.Describe(namespaceID, resourceID, describerSetting)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
formattedObj := ioutil.NopCloser(bytes.NewReader([]byte(obj)))
|
||||
readClosersWithLabel[formattedObj] = "describe"
|
||||
|
||||
return NewLogReadCloser(readClosersWithLabel), nil
|
||||
}
|
||||
|
||||
func (c *client) DeletePod(namespaceID, podID string) error {
|
||||
return c.client.CoreV1().Pods(namespaceID).Delete(podID, &metav1.DeleteOptions{})
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"github.com/weaveworks/scope/common/xfer"
|
||||
"github.com/weaveworks/scope/probe/controls"
|
||||
"github.com/weaveworks/scope/report"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// Control IDs used by the kubernetes integration.
|
||||
@@ -14,12 +16,19 @@ const (
|
||||
CloneVolumeSnapshot = report.KubernetesCloneVolumeSnapshot
|
||||
CreateVolumeSnapshot = report.KubernetesCreateVolumeSnapshot
|
||||
GetLogs = report.KubernetesGetLogs
|
||||
Describe = report.KubernetesDescribe
|
||||
DeletePod = report.KubernetesDeletePod
|
||||
DeleteVolumeSnapshot = report.KubernetesDeleteVolumeSnapshot
|
||||
ScaleUp = report.KubernetesScaleUp
|
||||
ScaleDown = report.KubernetesScaleDown
|
||||
)
|
||||
|
||||
// GroupName and version used by CRDs
|
||||
const (
|
||||
SnapshotGroupName = "volumesnapshot.external-storage.k8s.io"
|
||||
SnapshotVersion = "v1"
|
||||
)
|
||||
|
||||
// GetLogs is the control to get the logs for a kubernetes pod
|
||||
func (r *Reporter) GetLogs(req xfer.Request, namespaceID, podID string, containerNames []string) xfer.Response {
|
||||
readCloser, err := r.client.GetLogs(namespaceID, podID, containerNames)
|
||||
@@ -46,6 +55,90 @@ func (r *Reporter) GetLogs(req xfer.Request, namespaceID, podID string, containe
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Reporter) describePod(req xfer.Request, namespaceID, podID string, _ []string) xfer.Response {
|
||||
return r.describe(req, namespaceID, podID, ResourceMap["Pod"], apimeta.RESTMapping{})
|
||||
}
|
||||
|
||||
func (r *Reporter) describePVC(req xfer.Request, namespaceID, pvcID, _ string) xfer.Response {
|
||||
return r.describe(req, namespaceID, pvcID, ResourceMap["PersistentVolumeClaim"], apimeta.RESTMapping{})
|
||||
}
|
||||
|
||||
func (r *Reporter) describeDeployment(req xfer.Request, namespaceID, deploymentID string) xfer.Response {
|
||||
return r.describe(req, namespaceID, deploymentID, ResourceMap["Deployment"], apimeta.RESTMapping{})
|
||||
}
|
||||
|
||||
func (r *Reporter) describeService(req xfer.Request, namespaceID, serviceID string) xfer.Response {
|
||||
return r.describe(req, namespaceID, serviceID, ResourceMap["Service"], apimeta.RESTMapping{})
|
||||
}
|
||||
|
||||
func (r *Reporter) describeCronJob(req xfer.Request, namespaceID, cronJobID string) xfer.Response {
|
||||
return r.describe(req, namespaceID, cronJobID, ResourceMap["CronJob"], apimeta.RESTMapping{})
|
||||
}
|
||||
|
||||
func (r *Reporter) describePV(req xfer.Request, PVID string) xfer.Response {
|
||||
return r.describe(req, "", PVID, ResourceMap["PersistentVolume"], apimeta.RESTMapping{})
|
||||
}
|
||||
|
||||
func (r *Reporter) describeDaemonSet(req xfer.Request, namespaceID, daemonSetID string) xfer.Response {
|
||||
return r.describe(req, namespaceID, daemonSetID, ResourceMap["DaemonSet"], apimeta.RESTMapping{})
|
||||
}
|
||||
|
||||
func (r *Reporter) describeStatefulSet(req xfer.Request, namespaceID, statefulSetID string) xfer.Response {
|
||||
return r.describe(req, namespaceID, statefulSetID, ResourceMap["StatefulSet"], apimeta.RESTMapping{})
|
||||
}
|
||||
|
||||
func (r *Reporter) describeStoragelass(req xfer.Request, storageClassID string) xfer.Response {
|
||||
return r.describe(req, "", storageClassID, ResourceMap["StorageClass"], apimeta.RESTMapping{})
|
||||
}
|
||||
|
||||
func (r *Reporter) describeVolumeSnapshot(req xfer.Request, namespaceID, volumeSnapshotID, _, _ string) xfer.Response {
|
||||
restMapping := apimeta.RESTMapping{
|
||||
Resource: schema.GroupVersionResource{
|
||||
Group: SnapshotGroupName,
|
||||
Version: SnapshotVersion,
|
||||
Resource: "volumesnapshots",
|
||||
},
|
||||
}
|
||||
return r.describe(req, namespaceID, volumeSnapshotID, schema.GroupKind{}, restMapping)
|
||||
}
|
||||
|
||||
func (r *Reporter) describeVolumeSnapshotData(req xfer.Request, volumeSnapshotID string) xfer.Response {
|
||||
restMapping := apimeta.RESTMapping{
|
||||
Resource: schema.GroupVersionResource{
|
||||
Group: SnapshotGroupName,
|
||||
Version: SnapshotVersion,
|
||||
Resource: "volumesnapshotdatas",
|
||||
},
|
||||
}
|
||||
return r.describe(req, "", volumeSnapshotID, schema.GroupKind{}, restMapping)
|
||||
}
|
||||
|
||||
// GetLogs is the control to get the logs for a kubernetes pod
|
||||
func (r *Reporter) describe(req xfer.Request, namespaceID, resourceID string, groupKind schema.GroupKind, restMapping apimeta.RESTMapping) xfer.Response {
|
||||
readCloser, err := r.client.Describe(namespaceID, resourceID, groupKind, restMapping)
|
||||
if err != nil {
|
||||
return xfer.ResponseError(err)
|
||||
}
|
||||
|
||||
readWriter := struct {
|
||||
io.Reader
|
||||
io.Writer
|
||||
}{
|
||||
readCloser,
|
||||
ioutil.Discard,
|
||||
}
|
||||
id, pipe, err := controls.NewPipeFromEnds(nil, readWriter, r.pipes, req.AppID)
|
||||
if err != nil {
|
||||
return xfer.ResponseError(err)
|
||||
}
|
||||
pipe.OnClose(func() {
|
||||
readCloser.Close()
|
||||
})
|
||||
return xfer.Response{
|
||||
Pipe: id,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Reporter) cloneVolumeSnapshot(req xfer.Request, namespaceID, volumeSnapshotID, persistentVolumeClaimID, capacity string) xfer.Response {
|
||||
err := r.client.CloneVolumeSnapshot(namespaceID, volumeSnapshotID, persistentVolumeClaimID, capacity)
|
||||
if err != nil {
|
||||
@@ -80,6 +173,44 @@ func (r *Reporter) deleteVolumeSnapshot(req xfer.Request, namespaceID, volumeSna
|
||||
}
|
||||
}
|
||||
|
||||
// Describe will parse the nodeID and return response according to the node (resource) type.
|
||||
func (r *Reporter) Describe() func(xfer.Request) xfer.Response {
|
||||
return func(req xfer.Request) xfer.Response {
|
||||
var f func(req xfer.Request) xfer.Response
|
||||
_, tag, ok := report.ParseNodeID(req.NodeID)
|
||||
if !ok {
|
||||
return xfer.ResponseErrorf("Invalid ID: %s", req.NodeID)
|
||||
}
|
||||
switch tag {
|
||||
case "<pod>":
|
||||
f = r.CapturePod(r.describePod)
|
||||
case "<service>":
|
||||
f = r.CaptureService(r.describeService)
|
||||
case "<cronjob>":
|
||||
f = r.CaptureCronJob(r.describeCronJob)
|
||||
case "<deployment>":
|
||||
f = r.CaptureDeployment(r.describeDeployment)
|
||||
case "<daemonset>":
|
||||
f = r.CaptureDaemonSet(r.describeDaemonSet)
|
||||
case "<persistent_volume>":
|
||||
f = r.CapturePersistentVolume(r.describePV)
|
||||
case "<persistent_volume_claim>":
|
||||
f = r.CapturePersistentVolumeClaim(r.describePVC)
|
||||
case "<storage_class>":
|
||||
f = r.CaptureStorageClass(r.describeStoragelass)
|
||||
case "<statefulset>":
|
||||
f = r.CaptureStatefulSet(r.describeStatefulSet)
|
||||
case "<volume_snapshot>":
|
||||
f = r.CaptureVolumeSnapshot(r.describeVolumeSnapshot)
|
||||
case "<volume_snapshot_data>":
|
||||
f = r.CaptureVolumeSnapshotData(r.describeVolumeSnapshotData)
|
||||
default:
|
||||
return xfer.ResponseErrorf("Node not found: %s", req.NodeID)
|
||||
}
|
||||
return f(req)
|
||||
}
|
||||
}
|
||||
|
||||
// CapturePod is exported for testing
|
||||
func (r *Reporter) CapturePod(f func(xfer.Request, string, string, []string) xfer.Response) func(xfer.Request) xfer.Response {
|
||||
return func(req xfer.Request) xfer.Response {
|
||||
@@ -167,6 +298,155 @@ func (r *Reporter) CaptureVolumeSnapshot(f func(xfer.Request, string, string, st
|
||||
}
|
||||
}
|
||||
|
||||
// CaptureService is exported for testing
|
||||
func (r *Reporter) CaptureService(f func(xfer.Request, string, string) xfer.Response) func(xfer.Request) xfer.Response {
|
||||
return func(req xfer.Request) xfer.Response {
|
||||
uid, ok := report.ParseServiceNodeID(req.NodeID)
|
||||
if !ok {
|
||||
return xfer.ResponseErrorf("Invalid ID: %s", req.NodeID)
|
||||
}
|
||||
var service Service
|
||||
r.client.WalkServices(func(s Service) error {
|
||||
if s.UID() == uid {
|
||||
service = s
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if service == nil {
|
||||
return xfer.ResponseErrorf("Service not found: %s", uid)
|
||||
}
|
||||
return f(req, service.Namespace(), service.Name())
|
||||
}
|
||||
}
|
||||
|
||||
// CaptureDaemonSet is exported for testing
|
||||
func (r *Reporter) CaptureDaemonSet(f func(xfer.Request, string, string) xfer.Response) func(xfer.Request) xfer.Response {
|
||||
return func(req xfer.Request) xfer.Response {
|
||||
uid, ok := report.ParseDaemonSetNodeID(req.NodeID)
|
||||
if !ok {
|
||||
return xfer.ResponseErrorf("Invalid ID: %s", req.NodeID)
|
||||
}
|
||||
var daemonSet DaemonSet
|
||||
r.client.WalkDaemonSets(func(d DaemonSet) error {
|
||||
if d.UID() == uid {
|
||||
daemonSet = d
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if daemonSet == nil {
|
||||
return xfer.ResponseErrorf("Daemon Set not found: %s", uid)
|
||||
}
|
||||
return f(req, daemonSet.Namespace(), daemonSet.Name())
|
||||
}
|
||||
}
|
||||
|
||||
// CaptureCronJob is exported for testing
|
||||
func (r *Reporter) CaptureCronJob(f func(xfer.Request, string, string) xfer.Response) func(xfer.Request) xfer.Response {
|
||||
return func(req xfer.Request) xfer.Response {
|
||||
uid, ok := report.ParseCronJobNodeID(req.NodeID)
|
||||
if !ok {
|
||||
return xfer.ResponseErrorf("Invalid ID: %s", req.NodeID)
|
||||
}
|
||||
var cronJob CronJob
|
||||
r.client.WalkCronJobs(func(c CronJob) error {
|
||||
if c.UID() == uid {
|
||||
cronJob = c
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if cronJob == nil {
|
||||
return xfer.ResponseErrorf("Cron Job not found: %s", uid)
|
||||
}
|
||||
return f(req, cronJob.Namespace(), cronJob.Name())
|
||||
}
|
||||
}
|
||||
|
||||
// CaptureStatefulSet is exported for testing
|
||||
func (r *Reporter) CaptureStatefulSet(f func(xfer.Request, string, string) xfer.Response) func(xfer.Request) xfer.Response {
|
||||
return func(req xfer.Request) xfer.Response {
|
||||
uid, ok := report.ParseStatefulSetNodeID(req.NodeID)
|
||||
if !ok {
|
||||
return xfer.ResponseErrorf("Invalid ID: %s", req.NodeID)
|
||||
}
|
||||
var statefulSet StatefulSet
|
||||
r.client.WalkStatefulSets(func(s StatefulSet) error {
|
||||
if s.UID() == uid {
|
||||
statefulSet = s
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if statefulSet == nil {
|
||||
return xfer.ResponseErrorf("Stateful Set not found: %s", uid)
|
||||
}
|
||||
return f(req, statefulSet.Namespace(), statefulSet.Name())
|
||||
}
|
||||
}
|
||||
|
||||
// CaptureStorageClass is exported for testing
|
||||
func (r *Reporter) CaptureStorageClass(f func(xfer.Request, string) xfer.Response) func(xfer.Request) xfer.Response {
|
||||
return func(req xfer.Request) xfer.Response {
|
||||
uid, ok := report.ParseStorageClassNodeID(req.NodeID)
|
||||
if !ok {
|
||||
return xfer.ResponseErrorf("Invalid ID: %s", req.NodeID)
|
||||
}
|
||||
var storageClass StorageClass
|
||||
r.client.WalkStorageClasses(func(s StorageClass) error {
|
||||
if s.UID() == uid {
|
||||
storageClass = s
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if storageClass == nil {
|
||||
return xfer.ResponseErrorf("StorageClass not found: %s", uid)
|
||||
}
|
||||
return f(req, storageClass.Name())
|
||||
}
|
||||
}
|
||||
|
||||
// CapturePersistentVolume will return name of PV
|
||||
func (r *Reporter) CapturePersistentVolume(f func(xfer.Request, string) xfer.Response) func(xfer.Request) xfer.Response {
|
||||
return func(req xfer.Request) xfer.Response {
|
||||
uid, ok := report.ParsePersistentVolumeNodeID(req.NodeID)
|
||||
if !ok {
|
||||
return xfer.ResponseErrorf("Invalid ID: %s", req.NodeID)
|
||||
}
|
||||
// find persistentVolume by UID
|
||||
var persistentVolume PersistentVolume
|
||||
r.client.WalkPersistentVolumes(func(p PersistentVolume) error {
|
||||
if p.UID() == uid {
|
||||
persistentVolume = p
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if persistentVolume == nil {
|
||||
return xfer.ResponseErrorf("Persistent volume not found: %s", uid)
|
||||
}
|
||||
return f(req, persistentVolume.Name())
|
||||
}
|
||||
}
|
||||
|
||||
// CaptureVolumeSnapshotData will return name of volume snapshot data
|
||||
func (r *Reporter) CaptureVolumeSnapshotData(f func(xfer.Request, string) xfer.Response) func(xfer.Request) xfer.Response {
|
||||
return func(req xfer.Request) xfer.Response {
|
||||
uid, ok := report.ParseVolumeSnapshotDataNodeID(req.NodeID)
|
||||
if !ok {
|
||||
return xfer.ResponseErrorf("Invalid ID: %s", req.NodeID)
|
||||
}
|
||||
// find volume snapshotData by UID
|
||||
var volumeSnapshotData VolumeSnapshotData
|
||||
r.client.WalkVolumeSnapshotData(func(v VolumeSnapshotData) error {
|
||||
if v.UID() == uid {
|
||||
volumeSnapshotData = v
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if volumeSnapshotData == nil {
|
||||
return xfer.ResponseErrorf("Volume snapshot data not found: %s", uid)
|
||||
}
|
||||
return f(req, volumeSnapshotData.Name())
|
||||
}
|
||||
}
|
||||
|
||||
// ScaleUp is the control to scale up a deployment
|
||||
func (r *Reporter) ScaleUp(req xfer.Request, namespace, id string) xfer.Response {
|
||||
return xfer.ResponseError(r.client.ScaleUp(namespace, id))
|
||||
@@ -182,6 +462,7 @@ func (r *Reporter) registerControls() {
|
||||
CloneVolumeSnapshot: r.CaptureVolumeSnapshot(r.cloneVolumeSnapshot),
|
||||
CreateVolumeSnapshot: r.CapturePersistentVolumeClaim(r.createVolumeSnapshot),
|
||||
GetLogs: r.CapturePod(r.GetLogs),
|
||||
Describe: r.Describe(),
|
||||
DeletePod: r.CapturePod(r.deletePod),
|
||||
DeleteVolumeSnapshot: r.CaptureVolumeSnapshot(r.deleteVolumeSnapshot),
|
||||
ScaleUp: r.CaptureDeployment(r.ScaleUp),
|
||||
@@ -195,6 +476,7 @@ func (r *Reporter) deregisterControls() {
|
||||
CloneVolumeSnapshot,
|
||||
CreateVolumeSnapshot,
|
||||
GetLogs,
|
||||
Describe,
|
||||
DeletePod,
|
||||
DeleteVolumeSnapshot,
|
||||
ScaleUp,
|
||||
|
||||
@@ -85,7 +85,9 @@ func (cj *cronJob) GetNode(probeID string) report.Node {
|
||||
if cj.Status.LastScheduleTime != nil {
|
||||
latest[LastScheduled] = cj.Status.LastScheduleTime.Format(time.RFC3339Nano)
|
||||
}
|
||||
return cj.MetaNode(report.MakeCronJobNodeID(cj.UID())).WithLatests(latest)
|
||||
return cj.MetaNode(report.MakeCronJobNodeID(cj.UID())).
|
||||
WithLatests(latest).
|
||||
WithLatestActiveControls(Describe)
|
||||
}
|
||||
|
||||
func upgradeCronJob(legacy *batchv2alpha1.CronJob) *batchv1beta1.CronJob {
|
||||
|
||||
@@ -50,5 +50,5 @@ func (d *daemonSet) GetNode(probeID string) report.Node {
|
||||
MisscheduledReplicas: fmt.Sprint(d.Status.NumberMisscheduled),
|
||||
NodeType: "DaemonSet",
|
||||
report.ControlProbeID: probeID,
|
||||
})
|
||||
}).WithLatestActiveControls(Describe)
|
||||
}
|
||||
|
||||
@@ -61,5 +61,5 @@ func (d *deployment) GetNode(probeID string) report.Node {
|
||||
Strategy: string(d.Spec.Strategy.Type),
|
||||
report.ControlProbeID: probeID,
|
||||
NodeType: "Deployment",
|
||||
}).WithLatestActiveControls(ScaleUp, ScaleDown)
|
||||
}).WithLatestActiveControls(ScaleUp, ScaleDown, Describe)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
// PersistentVolume represent kubernetes PersistentVolume interface
|
||||
type PersistentVolume interface {
|
||||
Meta
|
||||
GetNode() report.Node
|
||||
GetNode(probeID string) report.Node
|
||||
GetAccessMode() string
|
||||
GetVolume() string
|
||||
GetStorageDriver() string
|
||||
@@ -64,18 +64,21 @@ func (p *persistentVolume) GetStorageDriver() string {
|
||||
}
|
||||
|
||||
// GetNode returns Persistent Volume as Node
|
||||
func (p *persistentVolume) GetNode() report.Node {
|
||||
func (p *persistentVolume) GetNode(probeID string) report.Node {
|
||||
latests := map[string]string{
|
||||
NodeType: "Persistent Volume",
|
||||
VolumeClaim: p.GetVolume(),
|
||||
StorageClassName: p.Spec.StorageClassName,
|
||||
Status: string(p.Status.Phase),
|
||||
AccessModes: p.GetAccessMode(),
|
||||
NodeType: "Persistent Volume",
|
||||
VolumeClaim: p.GetVolume(),
|
||||
StorageClassName: p.Spec.StorageClassName,
|
||||
Status: string(p.Status.Phase),
|
||||
AccessModes: p.GetAccessMode(),
|
||||
report.ControlProbeID: probeID,
|
||||
}
|
||||
|
||||
if p.GetStorageDriver() != "" {
|
||||
latests[StorageDriver] = p.GetStorageDriver()
|
||||
}
|
||||
|
||||
return p.MetaNode(report.MakePersistentVolumeNodeID(p.UID())).WithLatests(latests)
|
||||
return p.MetaNode(report.MakePersistentVolumeNodeID(p.UID())).
|
||||
WithLatests(latests).
|
||||
WithLatestActiveControls(Describe)
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ func (p *persistentVolumeClaim) GetNode(probeID string) report.Node {
|
||||
|
||||
return p.MetaNode(report.MakePersistentVolumeClaimNodeID(p.UID())).
|
||||
WithLatests(latests).
|
||||
WithLatestActiveControls(CreateVolumeSnapshot)
|
||||
WithLatestActiveControls(CreateVolumeSnapshot, Describe)
|
||||
}
|
||||
|
||||
// Selector returns all Persistent Volume Claim selector
|
||||
|
||||
@@ -107,7 +107,7 @@ func (p *pod) GetNode(probeID string) report.Node {
|
||||
|
||||
return p.MetaNode(report.MakePodNodeID(p.UID())).WithLatests(latests).
|
||||
WithParents(p.parents).
|
||||
WithLatestActiveControls(GetLogs, DeletePod)
|
||||
WithLatestActiveControls(GetLogs, DeletePod, Describe)
|
||||
}
|
||||
|
||||
func (p *pod) ContainerNames() []string {
|
||||
|
||||
@@ -168,6 +168,13 @@ var (
|
||||
Rank: 1,
|
||||
},
|
||||
}
|
||||
|
||||
DescribeControl = report.Control{
|
||||
ID: Describe,
|
||||
Human: "Describe",
|
||||
Icon: "fa fa-file-text",
|
||||
Rank: 2,
|
||||
}
|
||||
)
|
||||
|
||||
// Reporter generate Reports containing Container and ContainerImage topologies
|
||||
@@ -357,6 +364,7 @@ func (r *Reporter) serviceTopology() (report.Topology, []Service, error) {
|
||||
WithTableTemplates(TableTemplates)
|
||||
services = []Service{}
|
||||
)
|
||||
result.Controls.AddControl(DescribeControl)
|
||||
err := r.client.WalkServices(func(s Service) error {
|
||||
result.AddNode(s.GetNode(r.probeID))
|
||||
services = append(services, s)
|
||||
@@ -374,6 +382,7 @@ func (r *Reporter) deploymentTopology() (report.Topology, []Deployment, error) {
|
||||
deployments = []Deployment{}
|
||||
)
|
||||
result.Controls.AddControls(ScalingControls)
|
||||
result.Controls.AddControl(DescribeControl)
|
||||
|
||||
err := r.client.WalkDeployments(func(d Deployment) error {
|
||||
result.AddNode(d.GetNode(r.probeID))
|
||||
@@ -389,6 +398,7 @@ func (r *Reporter) daemonSetTopology() (report.Topology, []DaemonSet, error) {
|
||||
WithMetadataTemplates(DaemonSetMetadataTemplates).
|
||||
WithMetricTemplates(DaemonSetMetricTemplates).
|
||||
WithTableTemplates(TableTemplates)
|
||||
result.Controls.AddControl(DescribeControl)
|
||||
err := r.client.WalkDaemonSets(func(d DaemonSet) error {
|
||||
result.AddNode(d.GetNode(r.probeID))
|
||||
daemonSets = append(daemonSets, d)
|
||||
@@ -403,6 +413,7 @@ func (r *Reporter) statefulSetTopology() (report.Topology, []StatefulSet, error)
|
||||
WithMetadataTemplates(StatefulSetMetadataTemplates).
|
||||
WithMetricTemplates(StatefulSetMetricTemplates).
|
||||
WithTableTemplates(TableTemplates)
|
||||
result.Controls.AddControl(DescribeControl)
|
||||
err := r.client.WalkStatefulSets(func(s StatefulSet) error {
|
||||
result.AddNode(s.GetNode(r.probeID))
|
||||
statefulSets = append(statefulSets, s)
|
||||
@@ -417,6 +428,7 @@ func (r *Reporter) cronJobTopology() (report.Topology, []CronJob, error) {
|
||||
WithMetadataTemplates(CronJobMetadataTemplates).
|
||||
WithMetricTemplates(CronJobMetricTemplates).
|
||||
WithTableTemplates(TableTemplates)
|
||||
result.Controls.AddControl(DescribeControl)
|
||||
err := r.client.WalkCronJobs(func(c CronJob) error {
|
||||
result.AddNode(c.GetNode(r.probeID))
|
||||
cronJobs = append(cronJobs, c)
|
||||
@@ -430,8 +442,9 @@ func (r *Reporter) persistentVolumeTopology() (report.Topology, []PersistentVolu
|
||||
result := report.MakeTopology().
|
||||
WithMetadataTemplates(PersistentVolumeMetadataTemplates).
|
||||
WithTableTemplates(TableTemplates)
|
||||
result.Controls.AddControl(DescribeControl)
|
||||
err := r.client.WalkPersistentVolumes(func(p PersistentVolume) error {
|
||||
result.AddNode(p.GetNode())
|
||||
result.AddNode(p.GetNode(r.probeID))
|
||||
persistentVolumes = append(persistentVolumes, p)
|
||||
return nil
|
||||
})
|
||||
@@ -449,6 +462,7 @@ func (r *Reporter) persistentVolumeClaimTopology() (report.Topology, []Persisten
|
||||
Icon: "fa fa-camera",
|
||||
Rank: 0,
|
||||
})
|
||||
result.Controls.AddControl(DescribeControl)
|
||||
err := r.client.WalkPersistentVolumeClaims(func(p PersistentVolumeClaim) error {
|
||||
result.AddNode(p.GetNode(r.probeID))
|
||||
persistentVolumeClaims = append(persistentVolumeClaims, p)
|
||||
@@ -462,8 +476,9 @@ func (r *Reporter) storageClassTopology() (report.Topology, []StorageClass, erro
|
||||
result := report.MakeTopology().
|
||||
WithMetadataTemplates(StorageClassMetadataTemplates).
|
||||
WithTableTemplates(TableTemplates)
|
||||
result.Controls.AddControl(DescribeControl)
|
||||
err := r.client.WalkStorageClasses(func(p StorageClass) error {
|
||||
result.AddNode(p.GetNode())
|
||||
result.AddNode(p.GetNode(r.probeID))
|
||||
storageClasses = append(storageClasses, p)
|
||||
return nil
|
||||
})
|
||||
@@ -487,6 +502,7 @@ func (r *Reporter) volumeSnapshotTopology() (report.Topology, []VolumeSnapshot,
|
||||
Icon: "far fa-trash-alt",
|
||||
Rank: 1,
|
||||
})
|
||||
result.Controls.AddControl(DescribeControl)
|
||||
err := r.client.WalkVolumeSnapshots(func(p VolumeSnapshot) error {
|
||||
result.AddNode(p.GetNode(r.probeID))
|
||||
volumeSnapshots = append(volumeSnapshots, p)
|
||||
@@ -500,6 +516,7 @@ func (r *Reporter) volumeSnapshotDataTopology() (report.Topology, []VolumeSnapsh
|
||||
result := report.MakeTopology().
|
||||
WithMetadataTemplates(VolumeSnapshotDataMetadataTemplates).
|
||||
WithTableTemplates(TableTemplates)
|
||||
result.Controls.AddControl(DescribeControl)
|
||||
err := r.client.WalkVolumeSnapshotData(func(p VolumeSnapshotData) error {
|
||||
result.AddNode(p.GetNode(r.probeID))
|
||||
volumeSnapshotData = append(volumeSnapshotData, p)
|
||||
@@ -544,6 +561,7 @@ func (r *Reporter) podTopology(services []Service, deployments []Deployment, dae
|
||||
Confirmation: "Are you sure you want to delete this pod?",
|
||||
Rank: 1,
|
||||
})
|
||||
pods.Controls.AddControl(DescribeControl)
|
||||
for _, service := range services {
|
||||
selectors = append(selectors, match(
|
||||
service.Namespace(),
|
||||
|
||||
@@ -9,7 +9,9 @@ import (
|
||||
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
apiv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||
k8smeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/weaveworks/scope/common/xfer"
|
||||
@@ -196,6 +198,9 @@ func (c *mockClient) CreateVolumeSnapshot(namespaceID, persistentVolumeClaimID,
|
||||
func (c *mockClient) DeleteVolumeSnapshot(namespaceID, VolumeSnapshotID string) error {
|
||||
return nil
|
||||
}
|
||||
func (c *mockClient) Describe(namespaceID, resourceID string, groupKind schema.GroupKind, restMapping k8smeta.RESTMapping) (io.ReadCloser, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type mockPipeClient map[string]xfer.Pipe
|
||||
|
||||
|
||||
@@ -63,7 +63,9 @@ func (s *service) GetNode(probeID string) report.Node {
|
||||
}
|
||||
latest[Ports] = portStr[:len(portStr)-1]
|
||||
}
|
||||
return s.MetaNode(report.MakeServiceNodeID(s.UID())).WithLatests(latest)
|
||||
return s.MetaNode(report.MakeServiceNodeID(s.UID())).
|
||||
WithLatests(latest).
|
||||
WithLatestActiveControls(Describe)
|
||||
}
|
||||
|
||||
func (s *service) ClusterIP() string {
|
||||
|
||||
@@ -52,5 +52,7 @@ func (s *statefulSet) GetNode(probeID string) report.Node {
|
||||
if s.Status.ObservedGeneration != nil {
|
||||
latests[ObservedGeneration] = fmt.Sprint(*s.Status.ObservedGeneration)
|
||||
}
|
||||
return s.MetaNode(report.MakeStatefulSetNodeID(s.UID())).WithLatests(latests)
|
||||
return s.MetaNode(report.MakeStatefulSetNodeID(s.UID())).
|
||||
WithLatests(latests).
|
||||
WithLatestActiveControls(Describe)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
// StorageClass represent kubernetes StorageClass interface
|
||||
type StorageClass interface {
|
||||
Meta
|
||||
GetNode() report.Node
|
||||
GetNode(probeID string) report.Node
|
||||
}
|
||||
|
||||
// storageClass represents kubernetes storage classes
|
||||
@@ -23,10 +23,11 @@ func NewStorageClass(p *storagev1.StorageClass) StorageClass {
|
||||
}
|
||||
|
||||
// GetNode returns StorageClass as Node
|
||||
func (p *storageClass) GetNode() report.Node {
|
||||
func (p *storageClass) GetNode(probeID string) report.Node {
|
||||
return p.MetaNode(report.MakeStorageClassNodeID(p.UID())).WithLatests(map[string]string{
|
||||
NodeType: "Storage Class",
|
||||
Name: p.GetName(),
|
||||
Provisioner: p.Provisioner,
|
||||
})
|
||||
NodeType: "Storage Class",
|
||||
Name: p.GetName(),
|
||||
Provisioner: p.Provisioner,
|
||||
report.ControlProbeID: probeID,
|
||||
}).WithLatestActiveControls(Describe)
|
||||
}
|
||||
|
||||
@@ -56,5 +56,5 @@ func (p *volumeSnapshot) GetNode(probeID string) report.Node {
|
||||
VolumeClaim: p.GetVolumeName(),
|
||||
SnapshotData: p.Spec.SnapshotDataName,
|
||||
VolumeName: p.GetLabels()[SnapshotPVName],
|
||||
}).WithLatestActiveControls(CloneVolumeSnapshot, DeleteVolumeSnapshot)
|
||||
}).WithLatestActiveControls(CloneVolumeSnapshot, DeleteVolumeSnapshot, Describe)
|
||||
}
|
||||
|
||||
@@ -29,5 +29,5 @@ func (p *volumeSnapshotData) GetNode(probeID string) report.Node {
|
||||
NodeType: "Volume Snapshot Data",
|
||||
VolumeName: p.Spec.PersistentVolumeRef.Name,
|
||||
VolumeSnapshotName: p.Spec.VolumeSnapshotRef.Name,
|
||||
})
|
||||
}).WithLatestActiveControls(Describe)
|
||||
}
|
||||
|
||||
@@ -88,6 +88,7 @@ const (
|
||||
KubernetesVolumeCapacity = "kubernetes_volume_capacity"
|
||||
KubernetesCloneVolumeSnapshot = "kubernetes_clone_volume_snapshot"
|
||||
KubernetesDeleteVolumeSnapshot = "kubernetes_delete_volume_snapshot"
|
||||
KubernetesDescribe = "kubernetes_describe"
|
||||
// probe/awsecs
|
||||
ECSCluster = "ecs_cluster"
|
||||
ECSCreatedAt = "ecs_created_at"
|
||||
|
||||
Reference in New Issue
Block a user