Add control on PVC to take volume snapshot

Signed-off-by: Akash Srivastava <akash.srivastava@openebs.io>
This commit is contained in:
Akash Srivastava
2018-09-26 11:48:04 +05:30
parent 81a682893f
commit 8692a4d57d
6 changed files with 106 additions and 17 deletions

View File

@@ -3,6 +3,7 @@ package kubernetes
import (
"fmt"
"io"
"strings"
"sync"
"time"
@@ -10,6 +11,7 @@ import (
snapshotv1 "github.com/openebs/k8s-snapshot-client/snapshot/pkg/apis/volumesnapshot/v1"
snapshot "github.com/openebs/k8s-snapshot-client/snapshot/pkg/client/clientset/versioned"
"github.com/pborman/uuid"
log "github.com/sirupsen/logrus"
apiappsv1beta1 "k8s.io/api/apps/v1beta1"
apibatchv1 "k8s.io/api/batch/v1"
@@ -48,6 +50,7 @@ type Client interface {
WatchPods(f func(Event, Pod))
CreateVolumeSnapshot(namespaceID, persistentVolumeClaimID, capacity string) error
GetLogs(namespaceID, podID string, containerNames []string) (io.ReadCloser, error)
DeletePod(namespaceID, podID string) error
ScaleUp(resource, namespaceID, id string) error
@@ -427,6 +430,27 @@ func (c *client) WalkVolumeSnapshotData(f func(VolumeSnapshotData) error) error
return nil
}
func (c *client) CreateVolumeSnapshot(namespaceID, persistentVolumeClaimID, capacity string) error {
UID := strings.Split(uuid.New(), "-")
volumeSnapshot := &snapshotv1.VolumeSnapshot{
ObjectMeta: metav1.ObjectMeta{
Name: "snapshot-" + time.Now().Format("20060102150405") + "-" + UID[1],
Namespace: namespaceID,
Annotations: map[string]string{
"capacity": capacity,
},
},
Spec: snapshotv1.VolumeSnapshotSpec{
PersistentVolumeClaimName: persistentVolumeClaimID,
},
}
_, err := c.snapshotClient.VolumesnapshotV1().VolumeSnapshots(namespaceID).Create(volumeSnapshot)
if err != nil {
return err
}
return nil
}
func (c *client) GetLogs(namespaceID, podID string, containerNames []string) (io.ReadCloser, error) {
readClosersWithLabel := map[io.ReadCloser]string{}
for _, container := range containerNames {

View File

@@ -11,10 +11,11 @@ import (
// Control IDs used by the kubernetes integration.
const (
GetLogs = report.KubernetesGetLogs
DeletePod = report.KubernetesDeletePod
ScaleUp = report.KubernetesScaleUp
ScaleDown = report.KubernetesScaleDown
CreateVolumeSnapshot = report.KubernetesCreateVolumeSnapshot
GetLogs = report.KubernetesGetLogs
DeletePod = report.KubernetesDeletePod
ScaleUp = report.KubernetesScaleUp
ScaleDown = report.KubernetesScaleDown
)
// GetLogs is the control to get the logs for a kubernetes pod
@@ -43,6 +44,14 @@ func (r *Reporter) GetLogs(req xfer.Request, namespaceID, podID string, containe
}
}
func (r *Reporter) createVolumeSnapshot(req xfer.Request, namespaceID, persistentVolumeClaimID, capacity string) xfer.Response {
err := r.client.CreateVolumeSnapshot(namespaceID, persistentVolumeClaimID, capacity)
if err != nil {
return xfer.ResponseError(err)
}
return xfer.Response{}
}
func (r *Reporter) deletePod(req xfer.Request, namespaceID, podID string, _ []string) xfer.Response {
if err := r.client.DeletePod(namespaceID, podID); err != nil {
return xfer.ResponseError(err)
@@ -95,6 +104,28 @@ func (r *Reporter) CaptureDeployment(f func(xfer.Request, string, string) xfer.R
}
}
// CapturePersistentVolumeClaim will return name, namespace and capacity of PVC
func (r *Reporter) CapturePersistentVolumeClaim(f func(xfer.Request, string, string, string) xfer.Response) func(xfer.Request) xfer.Response {
return func(req xfer.Request) xfer.Response {
uid, ok := report.ParsePersistentVolumeClaimNodeID(req.NodeID)
if !ok {
return xfer.ResponseErrorf("Invalid ID: %s", req.NodeID)
}
// find persistentVolumeClaim by UID
var persistentVolumeClaim PersistentVolumeClaim
r.client.WalkPersistentVolumeClaims(func(p PersistentVolumeClaim) error {
if p.UID() == uid {
persistentVolumeClaim = p
}
return nil
})
if persistentVolumeClaim == nil {
return xfer.ResponseErrorf("Persistent volume claim not found: %s", uid)
}
return f(req, persistentVolumeClaim.Namespace(), persistentVolumeClaim.Name(), persistentVolumeClaim.GetCapacity())
}
}
// 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(report.Deployment, namespace, id))
@@ -107,16 +138,18 @@ func (r *Reporter) ScaleDown(req xfer.Request, namespace, id string) xfer.Respon
func (r *Reporter) registerControls() {
controls := map[string]xfer.ControlHandlerFunc{
GetLogs: r.CapturePod(r.GetLogs),
DeletePod: r.CapturePod(r.deletePod),
ScaleUp: r.CaptureDeployment(r.ScaleUp),
ScaleDown: r.CaptureDeployment(r.ScaleDown),
CreateVolumeSnapshot: r.CapturePersistentVolumeClaim(r.createVolumeSnapshot),
GetLogs: r.CapturePod(r.GetLogs),
DeletePod: r.CapturePod(r.deletePod),
ScaleUp: r.CaptureDeployment(r.ScaleUp),
ScaleDown: r.CaptureDeployment(r.ScaleDown),
}
r.handlerRegistry.Batch(nil, controls)
}
func (r *Reporter) deregisterControls() {
controls := []string{
CreateVolumeSnapshot,
GetLogs,
DeletePod,
ScaleUp,

View File

@@ -17,8 +17,9 @@ const (
type PersistentVolumeClaim interface {
Meta
Selector() (labels.Selector, error)
GetNode() report.Node
GetNode(string) report.Node
GetStorageClass() string
GetCapacity() string
}
// persistentVolumeClaim represents kubernetes Persistent Volume Claims
@@ -47,14 +48,32 @@ func (p *persistentVolumeClaim) GetStorageClass() string {
return storageClassName
}
// GetCapacity returns the storage size of PVC
func (p *persistentVolumeClaim) GetCapacity() string {
capacity := p.Spec.Resources.Requests[apiv1.ResourceStorage]
if capacity.String() != "" {
return capacity.String()
}
return ""
}
// GetNode returns Persistent Volume Claim as Node
func (p *persistentVolumeClaim) GetNode() report.Node {
return p.MetaNode(report.MakePersistentVolumeClaimNodeID(p.UID())).WithLatests(map[string]string{
NodeType: "Persistent Volume Claim",
Status: string(p.Status.Phase),
VolumeName: p.Spec.VolumeName,
StorageClassName: p.GetStorageClass(),
})
func (p *persistentVolumeClaim) GetNode(probeID string) report.Node {
latests := map[string]string{
NodeType: "Persistent Volume Claim",
Status: string(p.Status.Phase),
VolumeName: p.Spec.VolumeName,
StorageClassName: p.GetStorageClass(),
report.ControlProbeID: probeID,
}
if p.GetCapacity() != "" {
latests[VolumeCapacity] = p.GetCapacity()
}
return p.MetaNode(report.MakePersistentVolumeClaimNodeID(p.UID())).
WithLatests(latests).
WithLatestActiveControls(CreateVolumeSnapshot)
}
// Selector returns all Persistent Volume Claim selector

View File

@@ -36,6 +36,7 @@ const (
StorageDriver = report.KubernetesStorageDriver
VolumeSnapshotName = report.KubernetesVolumeSnapshotName
SnapshotData = report.KubernetesSnapshotData
VolumeCapacity = report.KubernetesVolumeCapacity
)
// Exposed for testing
@@ -124,6 +125,7 @@ var (
Status: {ID: Status, Label: "Status", From: report.FromLatest, Priority: 3},
VolumeName: {ID: VolumeName, Label: "Volume", From: report.FromLatest, Priority: 4},
StorageClassName: {ID: StorageClassName, Label: "Storage class", From: report.FromLatest, Priority: 5},
VolumeCapacity: {ID: VolumeCapacity, Label: "Capacity", From: report.FromLatest, Priority: 6},
}
StorageClassMetadataTemplates = report.MetadataTemplates{
@@ -463,8 +465,14 @@ func (r *Reporter) persistentVolumeClaimTopology() (report.Topology, []Persisten
result := report.MakeTopology().
WithMetadataTemplates(PersistentVolumeClaimMetadataTemplates).
WithTableTemplates(TableTemplates)
result.Controls.AddControl(report.Control{
ID: CreateVolumeSnapshot,
Human: "Create snapshot",
Icon: "fa-camera",
Rank: 0,
})
err := r.client.WalkPersistentVolumeClaims(func(p PersistentVolumeClaim) error {
result.AddNode(p.GetNode())
result.AddNode(p.GetNode(r.probeID))
persistentVolumeClaims = append(persistentVolumeClaims, p)
return nil
})

View File

@@ -187,6 +187,9 @@ func (c *mockClient) ScaleUp(resource, namespaceID, id string) error {
func (c *mockClient) ScaleDown(resource, namespaceID, id string) error {
return nil
}
func (c *mockClient) CreateVolumeSnapshot(namespaceID, persistentVolumeClaimID, capacity string) error {
return nil
}
type mockPipeClient map[string]xfer.Pipe

View File

@@ -83,6 +83,8 @@ const (
KubernetesStorageDriver = "kubernetes_storage_driver"
KubernetesVolumeSnapshotName = "kubernetes_volume_snapshot_name"
KubernetesSnapshotData = "kuberneets_snapshot_data"
KubernetesCreateVolumeSnapshot = "kubernetes_create_volume_snapshot"
KubernetesVolumeCapacity = "kubernetes_volume_capacity"
// probe/awsecs
ECSCluster = "ecs_cluster"
ECSCreatedAt = "ecs_created_at"