Skip to content

Commit 1e62519

Browse files
committed
fix: pv cannot be restored correctly
1 parent cea5778 commit 1e62519

File tree

7 files changed

+345
-55
lines changed

7 files changed

+345
-55
lines changed

go.mod

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ module github.com/openebs/velero-plugin
33
go 1.13
44

55
require (
6-
cloud.google.com/go v0.58.0 // indirect
7-
cloud.google.com/go/storage v1.9.0 // indirect
86
github.com/Azure/azure-pipeline-go v0.2.2 // indirect
97
github.com/Azure/azure-storage-blob-go v0.8.0 // indirect
108
github.com/aws/aws-sdk-go v1.35.24
@@ -16,6 +14,7 @@ require (
1614
github.com/onsi/ginkgo v1.15.2
1715
github.com/onsi/gomega v1.10.2
1816
github.com/openebs/api/v2 v2.3.0
17+
github.com/openebs/cstor-csi v1.12.0-RC1.0.20220712095109-ed7121554bd2
1918
github.com/openebs/maya v1.12.1-0.20210416090832-ad9c32f086d5
2019
github.com/openebs/zfs-localpv v1.6.1-0.20210504173514-62b3a0b7fe5d
2120
github.com/pkg/errors v0.9.1
@@ -40,6 +39,8 @@ replace (
4039
k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.20.2
4140
k8s.io/code-generator => k8s.io/code-generator v0.20.2
4241
k8s.io/component-base => k8s.io/component-base v0.20.2
42+
k8s.io/component-helpers => k8s.io/component-helpers v0.20.0
43+
k8s.io/controller-manager => k8s.io/controller-manager v0.20.0
4344
k8s.io/cri-api => k8s.io/cri-api v0.20.2
4445
k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.20.2
4546
k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.20.2
@@ -50,6 +51,7 @@ replace (
5051
k8s.io/kubelet => k8s.io/kubelet v0.20.2
5152
k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.20.2
5253
k8s.io/metrics => k8s.io/metrics v0.20.2
54+
k8s.io/mount-utils => k8s.io/mount-utils v0.20.0
5355
k8s.io/node-api => k8s.io/node-api v0.20.2
5456
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.20.2
5557
k8s.io/sample-cli-plugin => k8s.io/sample-cli-plugin v0.20.2

go.sum

Lines changed: 99 additions & 48 deletions
Large diffs are not rendered by default.

pkg/cstor/cstor.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,11 +446,20 @@ func (p *Plugin) CreateSnapshot(volumeID, volumeAZ string, tags map[string]strin
446446
}
447447

448448
if !p.local {
449-
// If cloud snapshot is configured then we need to backup PVC also
449+
// If cloud snapshot is configured then we need to backup PVC,PV, CVC also
450+
p.Log.Infof("backup PVC, PV, CVC first")
450451
err := p.backupPVC(volumeID)
451452
if err != nil {
452453
return "", errors.Wrapf(err, "failed to create backup for PVC")
453454
}
455+
err = p.backupPV(volumeID)
456+
if err != nil {
457+
return "", errors.Wrapf(err, "failed to create backup for PV")
458+
}
459+
err = p.backupCVC(volumeID)
460+
if err != nil {
461+
return "", errors.Wrapf(err, "failed to create backup for CVC")
462+
}
454463
}
455464

456465
p.Log.Infof("creating snapshot{%s}", bkpname)

pkg/cstor/cvc_operation.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
Copyright 2019 The OpenEBS Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cstor
18+
19+
import (
20+
"encoding/json"
21+
"fmt"
22+
23+
cstorv1 "github.com/openebs/api/v2/pkg/apis/cstor/v1"
24+
maya "github.com/openebs/cstor-csi/pkg/utils"
25+
"github.com/pkg/errors"
26+
k8serrors "k8s.io/apimachinery/pkg/api/errors"
27+
)
28+
29+
// (Kasakaze)todo: Determine whether it is csiVolume, if so, cvc must be backed up
30+
func (p *Plugin) backupCVC(volumeID string) error {
31+
vol := p.volumes[volumeID]
32+
33+
bkpCvc, err := maya.GetVolume(volumeID)
34+
if err != nil {
35+
if !k8serrors.IsNotFound(err) {
36+
return errors.Cause(err)
37+
}
38+
p.Log.Warnf("failed to get cvc, skip. %v", err)
39+
return nil
40+
}
41+
42+
data, err := json.MarshalIndent(bkpCvc, "", "\t")
43+
if err != nil {
44+
return errors.New("error doing json parsing")
45+
}
46+
47+
// pv backup file name
48+
filename := p.cl.GenerateRemoteFilename(vol.volname, vol.backupName)
49+
if filename == "" {
50+
return errors.New("error creating remote file name for pvc backup")
51+
}
52+
if ok := p.cl.Write(data, filename+".cvc"); !ok {
53+
return errors.New("failed to upload CVC")
54+
}
55+
56+
return nil
57+
}
58+
59+
// restoreCVC create CVC for given volume name
60+
// (Kasakaze)todo: Determine whether it is csiVolume, if so, cvc must be restored
61+
func (p *Plugin) restoreCVC(volumeID, pvcName, pvcNamespace, snapName string) error {
62+
// verify if the volume has already been created
63+
cvc, err := maya.GetVolume(volumeID)
64+
if err != nil {
65+
if !k8serrors.IsNotFound(err) {
66+
return errors.Cause(err)
67+
}
68+
}
69+
if err == nil && cvc != nil && cvc.DeletionTimestamp == nil {
70+
p.Log.Warn("cvc already exists, don't provision volume")
71+
return nil
72+
}
73+
74+
p.Log.Info("cvc does not exist, download and provision")
75+
rcvc, err := p.downloadCVC(volumeID, snapName)
76+
if err != nil {
77+
p.Log.Warnf("failed to download cvc, skip. %v", err)
78+
return nil
79+
}
80+
81+
var (
82+
size, _ = rcvc.Spec.Capacity.Storage().AsInt64()
83+
rCount = fmt.Sprint(rcvc.Spec.Provision.ReplicaCount)
84+
cspcName = rcvc.ObjectMeta.Labels["openebs.io/cstor-pool-cluster"]
85+
snapshotID = ""
86+
// (Kasakaze)todo: If the data is migrated to another cluster, the nodeID may not be the same
87+
nodeID = rcvc.Publish.NodeID
88+
policyName = rcvc.ObjectMeta.Labels["openebs.io/volume-policy"]
89+
)
90+
91+
err = maya.ProvisionVolume(size, volumeID, rCount,
92+
cspcName, snapshotID,
93+
nodeID, policyName, pvcName, pvcNamespace)
94+
if err != nil {
95+
return errors.Cause(err)
96+
}
97+
98+
return nil
99+
}
100+
101+
func (p *Plugin) downloadCVC(volumeID, snapName string) (*cstorv1.CStorVolumeConfig, error) {
102+
cvc := &cstorv1.CStorVolumeConfig{}
103+
104+
filename := p.cl.GenerateRemoteFilename(volumeID, snapName)
105+
filename += ".cvc"
106+
data, ok := p.cl.Read(filename)
107+
if !ok {
108+
return nil, errors.Errorf("failed to download CVC file=%s", filename)
109+
}
110+
111+
if err := json.Unmarshal(data, cvc); err != nil {
112+
return nil, errors.Errorf("failed to decode CVC file=%s", filename)
113+
}
114+
115+
return cvc, nil
116+
}

pkg/cstor/cvr_operation.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ func (p *Plugin) waitForAllCVRsToBeInValidStatus(vol *Volume, statuses []string)
5656
return errors.Errorf("Failed to fetch replicaCount for volume{%s}", vol.volname)
5757
}
5858

59+
p.Log.Infof("Waiting for all CVRs of PV(%s) to be ready, replicaCount=%d", vol.volname, replicaCount)
5960
if vol.isCSIVolume {
6061
return p.waitForCSIBasedCVRs(vol, replicaCount, statuses)
6162
}

pkg/cstor/pv_operation.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
v1alpha1 "github.com/openebs/maya/pkg/apis/openebs.io/v1alpha1"
2626
"github.com/pkg/errors"
2727
v1 "k8s.io/api/core/v1"
28+
k8serrors "k8s.io/apimachinery/pkg/api/errors"
2829
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2930
)
3031

@@ -70,6 +71,7 @@ func (p *Plugin) restoreVolumeFromCloud(vol *Volume, targetBackupName string) er
7071
err error
7172
)
7273

74+
p.Log.Info("Restoring volume data from cloud")
7375
if p.restoreAllSnapshots {
7476
// We are restoring from base backup to targeted Backup
7577
snapshotList, err = p.cl.GetSnapListFromCloud(vol.snapshotTag, p.getScheduleName(targetBackupName))
@@ -240,3 +242,97 @@ func contains(s []string, target string) bool {
240242

241243
return false
242244
}
245+
246+
// backupPV perform backup for given volume's PV
247+
func (p *Plugin) backupPV(volumeID string) error {
248+
vol := p.volumes[volumeID]
249+
250+
bkpPv, err := p.K8sClient.
251+
CoreV1().
252+
PersistentVolumes().
253+
Get(context.TODO(), vol.volname, metav1.GetOptions{})
254+
if err != nil {
255+
p.Log.Errorf("Error fetching PV(%s): %s", vol.volname, err.Error())
256+
return errors.New("failed to fetch PV")
257+
}
258+
259+
data, err := json.MarshalIndent(bkpPv, "", "\t")
260+
if err != nil {
261+
return errors.New("error doing json parsing")
262+
}
263+
264+
filename := p.cl.GenerateRemoteFilename(vol.volname, vol.backupName)
265+
if filename == "" {
266+
return errors.New("error creating remote file name for pvc backup")
267+
}
268+
269+
if ok := p.cl.Write(data, filename+".pv"); !ok {
270+
return errors.New("failed to upload PV")
271+
}
272+
273+
return nil
274+
}
275+
276+
// restorePV create PV for given volume name
277+
func (p *Plugin) restorePV(volumeID, snapName string) error {
278+
_, err := p.K8sClient.
279+
CoreV1().
280+
PersistentVolumes().
281+
Get(context.TODO(), volumeID, metav1.GetOptions{})
282+
if err == nil {
283+
p.Log.Infof("PV=%s already exists, skip restore", volumeID)
284+
return nil
285+
}
286+
if !k8serrors.IsNotFound(err) {
287+
return errors.Wrapf(err, "failed to get PV=%s", volumeID)
288+
}
289+
290+
pv, err := p.downloadPV(volumeID, snapName)
291+
if err != nil {
292+
return errors.Wrapf(err, "failed to download pv")
293+
}
294+
295+
// Add annotation PVCreatedByKey, with value 'restore' to PV
296+
pv.Annotations = make(map[string]string)
297+
pv.Annotations[v1alpha1.PVCreatedByKey] = "restore"
298+
pv.ManagedFields = nil
299+
pv.Finalizers = nil
300+
if pv.Spec.ClaimRef != nil {
301+
pv.Spec.ClaimRef.ResourceVersion = ""
302+
pv.Spec.ClaimRef.UID = ""
303+
}
304+
pv.CreationTimestamp = metav1.Time{}
305+
pv.ResourceVersion = ""
306+
pv.UID = ""
307+
pv.Status = v1.PersistentVolumeStatus{}
308+
309+
_, err = p.K8sClient.
310+
CoreV1().
311+
PersistentVolumes().
312+
Create(context.TODO(), pv, metav1.CreateOptions{})
313+
if err != nil {
314+
if !k8serrors.IsAlreadyExists(err) {
315+
return errors.Wrapf(err, "failed to create PV=%s", pv.Name)
316+
}
317+
p.Log.Infof("PV=%s already exists, skip restore", pv.Name)
318+
}
319+
320+
return nil
321+
}
322+
323+
func (p *Plugin) downloadPV(volumeID, snapName string) (*v1.PersistentVolume, error) {
324+
pv := &v1.PersistentVolume{}
325+
326+
filename := p.cl.GenerateRemoteFilename(volumeID, snapName)
327+
328+
data, ok := p.cl.Read(filename + ".pv")
329+
if !ok {
330+
return nil, errors.Errorf("failed to download PV file=%s", filename+".pv")
331+
}
332+
333+
if err := json.Unmarshal(data, pv); err != nil {
334+
return nil, errors.Errorf("failed to decode pv file=%s", filename+".pv")
335+
}
336+
337+
return pv, nil
338+
}

pkg/cstor/pvc_operation.go

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"github.com/pkg/errors"
2727
v1 "k8s.io/api/core/v1"
2828
k8serrors "k8s.io/apimachinery/pkg/api/errors"
29+
2930
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3031
"k8s.io/apimachinery/pkg/util/wait"
3132
)
@@ -128,12 +129,22 @@ func (p *Plugin) createPVC(volumeID, snapName string) (*Volume, error) {
128129
return newVol, nil
129130
}
130131

131-
p.Log.Infof("Creating PVC for volumeID:%s snapshot:%s in namespace=%s", volumeID, snapName, targetedNs)
132+
// restore PV so that it can be bound to PVC
133+
p.Log.Infof("Creating PV(%s) snapshot:%s", volumeID, snapName)
134+
if err := p.restorePV(volumeID, snapName); err != nil {
135+
return nil, errors.Wrapf(err, "failed to restore PV=%s", pvc.Spec.VolumeName)
136+
}
137+
138+
// pending until the pv is bound to pvc
139+
p.Log.Infof("Creating PVC with specified volumeID:%s, snapshot:%s in namespace=%s", volumeID, snapName, targetedNs)
132140

133141
pvc.Annotations = make(map[string]string)
134142
// Add annotation PVCreatedByKey, with value 'restore' to PVC
135143
// So that Maya-APIServer skip updating target IPAddress in CVR
136144
pvc.Annotations[v1alpha1.PVCreatedByKey] = "restore"
145+
146+
// Specify src pvname
147+
pvc.Spec.VolumeName = volumeID
137148
rpvc, err := p.K8sClient.
138149
CoreV1().
139150
PersistentVolumeClaims(pvc.Namespace).
@@ -142,6 +153,10 @@ func (p *Plugin) createPVC(volumeID, snapName string) (*Volume, error) {
142153
return nil, errors.Wrapf(err, "failed to create PVC=%s/%s", pvc.Namespace, pvc.Name)
143154
}
144155

156+
p.Log.Infof("Creating CVC(%s) snapshot:%s", volumeID, snapName)
157+
if err := p.restoreCVC(volumeID, rpvc.Name, rpvc.Namespace, snapName); err != nil {
158+
return nil, errors.Wrapf(err, "failed to restore CVC=%s", volumeID)
159+
}
145160
for cnt := 0; cnt < PVCWaitCount; cnt++ {
146161
pvc, err = p.K8sClient.
147162
CoreV1().
@@ -157,7 +172,7 @@ func (p *Plugin) createPVC(volumeID, snapName string) (*Volume, error) {
157172
return nil, errors.Wrapf(err, "failed to create PVC=%s/%s", rpvc.Namespace, rpvc.Name)
158173
}
159174
if pvc.Status.Phase == v1.ClaimBound {
160-
p.Log.Infof("PVC(%v) created..", pvc.Name)
175+
p.Log.Infof("PVC(%v) created, PV(%s) bound", pvc.Name, volumeID)
161176
vol = &Volume{
162177
volname: pvc.Spec.VolumeName,
163178
snapshotTag: volumeID,
@@ -168,6 +183,7 @@ func (p *Plugin) createPVC(volumeID, snapName string) (*Volume, error) {
168183
p.volumes[vol.volname] = vol
169184
break
170185
}
186+
p.Log.Debugf("PV(%v) is not bound yet..", volumeID)
171187
time.Sleep(PVCCheckInterval)
172188
}
173189

@@ -182,12 +198,10 @@ func (p *Plugin) createPVC(volumeID, snapName string) (*Volume, error) {
182198
p.Log.Errorf("Failed to get PV{%s}", vol.volname)
183199
return nil, errors.Wrapf(err, "failed to get pv=%s", vol.volname)
184200
}
185-
186201
vol.isCSIVolume = isCSIPv(*pv)
187202
if err = p.waitForAllCVRs(vol); err != nil {
188203
return nil, err
189204
}
190-
191205
// CVRs are created and updated, now we can remove the annotation 'PVCreatedByKey' from PVC
192206
if err = p.removePVCAnnotationKey(pvc, v1alpha1.PVCreatedByKey); err != nil {
193207
p.Log.Warningf("Failed to remove restore annotation from PVC=%s/%s err=%s", pvc.Namespace, pvc.Name, err)
@@ -207,6 +221,7 @@ func (p *Plugin) getVolumeFromPVC(pvc v1.PersistentVolumeClaim) (*Volume, error)
207221
Get(context.TODO(), pvc.Name, metav1.GetOptions{})
208222
if err != nil {
209223
if k8serrors.IsNotFound(err) {
224+
p.Log.Warnf("PVC{%s} not found", pvc.Name)
210225
return nil, nil
211226
}
212227
return nil, errors.Wrapf(err, "failed to fetch PVC{%s}", pvc.Name)

0 commit comments

Comments
 (0)