@@ -17,6 +17,7 @@ import (
17
17
18
18
validation "github.com/go-ozzo/ozzo-validation"
19
19
"github.com/opentracing/opentracing-go"
20
+ "github.com/sirupsen/logrus"
20
21
"golang.org/x/xerrors"
21
22
"google.golang.org/grpc"
22
23
"google.golang.org/grpc/codes"
@@ -29,12 +30,15 @@ import (
29
30
corev1 "k8s.io/api/core/v1"
30
31
k8serr "k8s.io/apimachinery/pkg/api/errors"
31
32
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
33
+ "k8s.io/apimachinery/pkg/fields"
32
34
"k8s.io/apimachinery/pkg/labels"
33
35
"k8s.io/apimachinery/pkg/runtime"
34
36
"k8s.io/apimachinery/pkg/types"
35
37
"k8s.io/apimachinery/pkg/util/wait"
38
+ "k8s.io/apimachinery/pkg/watch"
36
39
"k8s.io/client-go/kubernetes"
37
40
covev1client "k8s.io/client-go/kubernetes/typed/core/v1"
41
+ "k8s.io/client-go/tools/cache"
38
42
"k8s.io/client-go/tools/record"
39
43
"k8s.io/client-go/util/retry"
40
44
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -55,6 +59,7 @@ import (
55
59
56
60
volumesnapshotv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
57
61
volumesnapshotclientv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/clientset/versioned"
62
+ watchtools "k8s.io/client-go/tools/watch"
58
63
)
59
64
60
65
// Manager is a kubernetes backed implementation of a workspace manager
@@ -1522,6 +1527,96 @@ func (m *Manager) connectToWorkspaceDaemon(ctx context.Context, wso workspaceObj
1522
1527
return wsdaemon .NewWorkspaceContentServiceClient (conn ), nil
1523
1528
}
1524
1529
1530
+ func (m * Manager ) createWorkspaceSnapshotFromPVC (ctx context.Context , pvcName string , pvcVolumeSnapshotName string , pvcVolumeSnapshotClassName string , workspaceID string , labels map [string ]string ) error {
1531
+ // create snapshot object out of PVC
1532
+ volumeSnapshot := & volumesnapshotv1.VolumeSnapshot {
1533
+ ObjectMeta : metav1.ObjectMeta {
1534
+ Name : pvcVolumeSnapshotName ,
1535
+ Namespace : m .Config .Namespace ,
1536
+ Annotations : map [string ]string {workspaceIDAnnotation : workspaceID },
1537
+ Labels : labels ,
1538
+ },
1539
+ Spec : volumesnapshotv1.VolumeSnapshotSpec {
1540
+ Source : volumesnapshotv1.VolumeSnapshotSource {
1541
+ PersistentVolumeClaimName : & pvcName ,
1542
+ },
1543
+ VolumeSnapshotClassName : & pvcVolumeSnapshotClassName ,
1544
+ },
1545
+ }
1546
+
1547
+ err := m .Clientset .Create (ctx , volumeSnapshot )
1548
+ if err != nil && ! k8serr .IsAlreadyExists (err ) {
1549
+ err = xerrors .Errorf ("cannot create volumesnapshot: %v" , err )
1550
+ return err
1551
+ }
1552
+ return nil
1553
+ }
1554
+
1555
+ func (m * Manager ) waitForWorkspaceVolumeSnapshotReady (ctx context.Context , pvcVolumeSnapshotName string , log * logrus.Entry ) (pvcVolumeSnapshotContentName string , readyVolumeSnapshot bool , err error ) {
1556
+ log = log .WithField ("VolumeSnapshot.Name" , pvcVolumeSnapshotName )
1557
+
1558
+ var volumeSnapshotWatcher * watchtools.RetryWatcher
1559
+ volumeSnapshotWatcher , err = watchtools .NewRetryWatcher ("1" , & cache.ListWatch {
1560
+ WatchFunc : func (options metav1.ListOptions ) (watch.Interface , error ) {
1561
+ return m .VolumeSnapshotClient .SnapshotV1 ().VolumeSnapshots (m .Config .Namespace ).Watch (ctx , metav1.ListOptions {
1562
+ FieldSelector : fields .OneTermEqualSelector ("metadata.name" , pvcVolumeSnapshotName ).String (),
1563
+ })
1564
+ },
1565
+ })
1566
+ if err != nil {
1567
+ log .WithError (err ).Info ("fall back to exponential backoff retry" )
1568
+ // we can not create a retry watcher, we fall back to exponential backoff retry
1569
+ backoff := wait.Backoff {
1570
+ Steps : 30 ,
1571
+ Duration : 100 * time .Millisecond ,
1572
+ Factor : 1.5 ,
1573
+ Jitter : 0.1 ,
1574
+ Cap : 10 * time .Minute ,
1575
+ }
1576
+ err = wait .ExponentialBackoff (backoff , func () (bool , error ) {
1577
+ var vs volumesnapshotv1.VolumeSnapshot
1578
+ err := m .Clientset .Get (ctx , types.NamespacedName {Namespace : m .Config .Namespace , Name : pvcVolumeSnapshotName }, & vs )
1579
+ if err != nil {
1580
+ if k8serr .IsNotFound (err ) {
1581
+ // volumesnapshot doesn't exist yet, retry again
1582
+ return false , nil
1583
+ }
1584
+ log .WithError (err ).Error ("was unable to get volume snapshot" )
1585
+ return false , err
1586
+ }
1587
+ if vs .Status != nil && vs .Status .ReadyToUse != nil && * vs .Status .ReadyToUse && vs .Status .BoundVolumeSnapshotContentName != nil {
1588
+ pvcVolumeSnapshotContentName = * vs .Status .BoundVolumeSnapshotContentName
1589
+ return true , nil
1590
+ }
1591
+ return false , nil
1592
+ })
1593
+ if err != nil {
1594
+ log .WithError (err ).Errorf ("failed while waiting for volume snapshot to get ready" )
1595
+ return "" , false , err
1596
+ }
1597
+ readyVolumeSnapshot = true
1598
+ } else {
1599
+ for event := range volumeSnapshotWatcher .ResultChan () {
1600
+ vs , ok := event .Object .(* volumesnapshotv1.VolumeSnapshot )
1601
+ if ! ok {
1602
+ log .Errorf ("unexpected type assertion %T" , event .Object )
1603
+ continue
1604
+ }
1605
+
1606
+ if vs != nil && vs .Status != nil && vs .Status .ReadyToUse != nil && * vs .Status .ReadyToUse && vs .Status .BoundVolumeSnapshotContentName != nil {
1607
+ pvcVolumeSnapshotContentName = * vs .Status .BoundVolumeSnapshotContentName
1608
+ readyVolumeSnapshot = true
1609
+ break
1610
+ }
1611
+ }
1612
+
1613
+ // stop the volume snapshot retry watcher
1614
+ volumeSnapshotWatcher .Stop ()
1615
+ }
1616
+
1617
+ return pvcVolumeSnapshotContentName , readyVolumeSnapshot , nil
1618
+ }
1619
+
1525
1620
// newWssyncConnectionFactory creates a new wsdaemon connection factory based on the wsmanager configuration
1526
1621
func newWssyncConnectionFactory (managerConfig config.Configuration ) (grpcpool.Factory , error ) {
1527
1622
cfg := managerConfig .WorkspaceDaemon
0 commit comments