Skip to content

Commit 1972a9a

Browse files
suaas21tamalsaha
authored andcommitted
VolumeSnapshot (#787)
Tasks: - [x] Create VolumeSnapshot - [x] Restore VolumeSnapshot
1 parent 3f1f4cf commit 1972a9a

File tree

3 files changed

+426
-0
lines changed

3 files changed

+426
-0
lines changed

create_volumesnapshot.go

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
package cmds
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
"time"
7+
8+
"github.com/appscode/go/log"
9+
vs "github.com/kubernetes-csi/external-snapshotter/pkg/apis/volumesnapshot/v1alpha1"
10+
vs_cs "github.com/kubernetes-csi/external-snapshotter/pkg/client/clientset/versioned"
11+
"github.com/spf13/cobra"
12+
appsv1 "k8s.io/api/apps/v1"
13+
corev1 "k8s.io/api/core/v1"
14+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
15+
"k8s.io/client-go/kubernetes"
16+
"k8s.io/client-go/tools/clientcmd"
17+
"kmodules.xyz/client-go/meta"
18+
"stash.appscode.dev/stash/apis"
19+
"stash.appscode.dev/stash/apis/stash/v1beta1"
20+
cs "stash.appscode.dev/stash/client/clientset/versioned"
21+
"stash.appscode.dev/stash/pkg/restic"
22+
"stash.appscode.dev/stash/pkg/status"
23+
"stash.appscode.dev/stash/pkg/util"
24+
)
25+
26+
type VSoption struct {
27+
name string
28+
namespace string
29+
kubeClient kubernetes.Interface
30+
stashClient cs.Interface
31+
snapshotClient vs_cs.Interface
32+
}
33+
34+
func NewCmdCreateVolumeSnapshot() *cobra.Command {
35+
var (
36+
masterURL string
37+
kubeconfigPath string
38+
opt = VSoption{
39+
namespace: meta.Namespace(),
40+
}
41+
)
42+
43+
cmd := &cobra.Command{
44+
Use: "create-vs",
45+
Short: "Take snapshot of PersistentVolumeClaims",
46+
DisableAutoGenTag: true,
47+
Run: func(cmd *cobra.Command, args []string) {
48+
config, err := clientcmd.BuildConfigFromFlags(masterURL, kubeconfigPath)
49+
if err != nil {
50+
log.Fatalf("Could not get Kubernetes config: %s", err)
51+
}
52+
opt.kubeClient = kubernetes.NewForConfigOrDie(config)
53+
opt.stashClient = cs.NewForConfigOrDie(config)
54+
opt.snapshotClient = vs_cs.NewForConfigOrDie(config)
55+
56+
err = opt.CreateVolumeSnapshot()
57+
if err != nil {
58+
log.Fatal(err)
59+
}
60+
},
61+
}
62+
cmd.Flags().StringVar(&masterURL, "master", "", "The address of the Kubernetes API server (overrides any value in kubeconfig)")
63+
cmd.Flags().StringVar(&kubeconfigPath, "kubeconfig", "", "Path to kubeconfig file with authorization information (the master location is set by the master flag).")
64+
cmd.Flags().StringVar(&opt.name, "backupsession.name", "", "Set BackupSession Name")
65+
return cmd
66+
}
67+
68+
func (opt *VSoption) CreateVolumeSnapshot() error {
69+
// Start clock to measure total session duration
70+
startTime := time.Now()
71+
backupSession, err := opt.stashClient.StashV1beta1().BackupSessions(opt.namespace).Get(opt.name, metav1.GetOptions{})
72+
if err != nil {
73+
return err
74+
}
75+
backupConfiguration, err := opt.stashClient.StashV1beta1().BackupConfigurations(opt.namespace).Get(backupSession.Spec.BackupConfiguration.Name, metav1.GetOptions{})
76+
if err != nil {
77+
return err
78+
}
79+
if backupConfiguration == nil {
80+
return fmt.Errorf("BackupConfiguration is nil")
81+
}
82+
if backupConfiguration.Spec.Target == nil {
83+
return fmt.Errorf("backupConfiguration target is nil")
84+
}
85+
86+
kind := backupConfiguration.Spec.Target.Ref.Kind
87+
name := backupConfiguration.Spec.Target.Ref.Name
88+
namespace := backupConfiguration.Namespace
89+
90+
var (
91+
pvcList []string
92+
)
93+
94+
switch kind {
95+
case apis.KindDeployment:
96+
deployment, err := opt.kubeClient.AppsV1().Deployments(namespace).Get(name, metav1.GetOptions{})
97+
if err != nil {
98+
return err
99+
}
100+
pvcList = getPVCs(deployment.Spec.Template.Spec.Volumes)
101+
102+
case apis.KindDaemonSet:
103+
daemon, err := opt.kubeClient.AppsV1().DaemonSets(namespace).Get(name, metav1.GetOptions{})
104+
if err != nil {
105+
return err
106+
}
107+
pvcList = getPVCs(daemon.Spec.Template.Spec.Volumes)
108+
109+
case apis.KindReplicationController:
110+
rc, err := opt.kubeClient.CoreV1().ReplicationControllers(namespace).Get(name, metav1.GetOptions{})
111+
if err != nil {
112+
return err
113+
}
114+
pvcList = getPVCs(rc.Spec.Template.Spec.Volumes)
115+
116+
case apis.KindReplicaSet:
117+
rs, err := opt.kubeClient.AppsV1().ReplicaSets(namespace).Get(name, metav1.GetOptions{})
118+
if err != nil {
119+
return err
120+
}
121+
pvcList = getPVCs(rs.Spec.Template.Spec.Volumes)
122+
123+
case apis.KindStatefulSet:
124+
ss, err := opt.kubeClient.AppsV1().StatefulSets(namespace).Get(name, metav1.GetOptions{})
125+
if err != nil {
126+
return err
127+
}
128+
pvcList = getPVCsForStatefulset(ss.Spec.VolumeClaimTemplates, ss, backupConfiguration.Spec.Target.Replicas)
129+
130+
case apis.KindPersistentVolumeClaim:
131+
pvcList = []string{name}
132+
}
133+
134+
objectMeta := []metav1.ObjectMeta{}
135+
136+
for _, pvcName := range pvcList {
137+
parts := strings.Split(backupSession.Name, "-")
138+
volumeSnapshot := opt.getVolumeSnapshotDefinition(backupConfiguration, pvcName, parts[len(parts)-1])
139+
vs, err := opt.snapshotClient.VolumesnapshotV1alpha1().VolumeSnapshots(namespace).Create(&volumeSnapshot)
140+
if err != nil {
141+
return err
142+
}
143+
objectMeta = append(objectMeta, vs.ObjectMeta)
144+
}
145+
146+
for i, pvcName := range pvcList {
147+
err = util.WaitUntilVolumeSnapshotReady(opt.snapshotClient, objectMeta[i])
148+
if err != nil {
149+
return err
150+
}
151+
// Update Backup Session
152+
o := status.UpdateStatusOptions{
153+
KubeClient: opt.kubeClient,
154+
StashClient: opt.stashClient.(*cs.Clientset),
155+
Namespace: opt.namespace,
156+
BackupSession: opt.name,
157+
}
158+
backupOutput := restic.BackupOutput{
159+
HostBackupStats: v1beta1.HostBackupStats{
160+
Hostname: pvcName,
161+
Phase: v1beta1.HostBackupSucceeded,
162+
},
163+
}
164+
// Volume Snapshot complete. Read current time and calculate total backup duration.
165+
endTime := time.Now()
166+
backupOutput.HostBackupStats.Duration = endTime.Sub(startTime).String()
167+
168+
err = o.UpdatePostBackupStatus(&backupOutput)
169+
if err != nil {
170+
return err
171+
}
172+
}
173+
return nil
174+
}
175+
176+
func (opt *VSoption) getVolumeSnapshotDefinition(backupConfiguration *v1beta1.BackupConfiguration, pvcName string, timestamp string) (volumeSnapshot vs.VolumeSnapshot) {
177+
return vs.VolumeSnapshot{
178+
ObjectMeta: metav1.ObjectMeta{
179+
Name: fmt.Sprintf("%s-%s", pvcName, timestamp),
180+
Namespace: backupConfiguration.Namespace,
181+
},
182+
Spec: vs.VolumeSnapshotSpec{
183+
VolumeSnapshotClassName: &backupConfiguration.Spec.Target.VolumeSnapshotClassName,
184+
Source: &corev1.TypedLocalObjectReference{
185+
Kind: apis.KindPersistentVolumeClaim,
186+
Name: pvcName,
187+
},
188+
},
189+
}
190+
191+
}
192+
193+
func getPVCs(volList []corev1.Volume) []string {
194+
pvcList := make([]string, 0)
195+
for _, vol := range volList {
196+
if vol.PersistentVolumeClaim != nil {
197+
pvcList = append(pvcList, vol.PersistentVolumeClaim.ClaimName)
198+
}
199+
}
200+
return pvcList
201+
}
202+
203+
func getPVCsForStatefulset(volList []corev1.PersistentVolumeClaim, ss *appsv1.StatefulSet, replicas *int32) []string {
204+
pvcList := make([]string, 0)
205+
var rep *int32
206+
if replicas != nil {
207+
rep = replicas
208+
} else {
209+
rep = ss.Spec.Replicas
210+
}
211+
for i := int32(0); i < *rep; i++ {
212+
podName := fmt.Sprintf("%v-%v", ss.Name, i)
213+
for _, vol := range volList {
214+
pvcList = append(pvcList, fmt.Sprintf("%v-%v", vol.Name, podName))
215+
}
216+
}
217+
return pvcList
218+
219+
}

0 commit comments

Comments
 (0)