Skip to content

Commit 265fecd

Browse files
authored
Merge pull request #123 from xing-yang/datasource2
Restore Volume from Snapshot
2 parents 4d8a86c + c736242 commit 265fecd

File tree

8,985 files changed

+485255
-252257
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

8,985 files changed

+485255
-252257
lines changed

Gopkg.lock

Lines changed: 248 additions & 67 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Gopkg.toml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@
3232
branch = "master"
3333
name = "github.com/golang/glog"
3434

35+
[[constraint]]
36+
branch = "master"
37+
name = "github.com/kubernetes-csi/external-snapshotter"
38+
3539
[[constraint]]
3640
branch = "master"
3741
name = "github.com/kubernetes-incubator/external-storage"
@@ -42,12 +46,12 @@
4246

4347
[[constraint]]
4448
name = "k8s.io/apimachinery"
45-
version = "kubernetes-1.10.0-beta.1"
49+
version = "kubernetes-1.12.0-alpha.0"
4650

4751
[[constraint]]
4852
name = "k8s.io/client-go"
49-
version = "v6.0.0"
53+
version = "kubernetes-1.12.0-alpha.0"
5054

5155
[[constraint]]
5256
name = "k8s.io/api"
53-
version = "kubernetes-1.10.0-beta.1"
57+
branch = "master"

cmd/csi-provisioner/csi-provisioner.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/golang/glog"
2828

2929
ctrl "github.com/kubernetes-csi/external-provisioner/pkg/controller"
30+
snapclientset "github.com/kubernetes-csi/external-snapshotter/pkg/client/clientset/versioned"
3031
"github.com/kubernetes-incubator/external-storage/lib/controller"
3132

3233
"google.golang.org/grpc"
@@ -86,6 +87,11 @@ func init() {
8687
if err != nil {
8788
glog.Fatalf("Failed to create client: %v", err)
8889
}
90+
// snapclientset.NewForConfig creates a new Clientset for VolumesnapshotV1alpha1Client
91+
snapClient, err := snapclientset.NewForConfig(config)
92+
if err != nil {
93+
glog.Fatalf("Failed to create snapshot client: %v", err)
94+
}
8995

9096
// The controller needs to know what the server version is because out-of-tree
9197
// provisioners aren't officially supported until 1.5
@@ -112,7 +118,7 @@ func init() {
112118
}
113119
// Create the provisioner: it implements the Provisioner interface expected by
114120
// the controller
115-
csiProvisioner := ctrl.NewCSIProvisioner(clientset, *csiEndpoint, *connectionTimeout, identity, *volumeNamePrefix, *volumeNameUUIDLength, grpcClient)
121+
csiProvisioner := ctrl.NewCSIProvisioner(clientset, *csiEndpoint, *connectionTimeout, identity, *volumeNamePrefix, *volumeNameUUIDLength, grpcClient, snapClient)
116122
provisionController = controller.NewProvisionController(
117123
clientset,
118124
*provisioner,

pkg/controller/controller.go

Lines changed: 135 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ import (
3232

3333
"github.com/kubernetes-incubator/external-storage/lib/controller"
3434

35+
snapapi "github.com/kubernetes-csi/external-snapshotter/pkg/apis/volumesnapshot/v1alpha1"
36+
snapclientset "github.com/kubernetes-csi/external-snapshotter/pkg/client/clientset/versioned"
37+
3538
"k8s.io/api/core/v1"
3639
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3740
"k8s.io/apimachinery/pkg/util/sets"
@@ -69,13 +72,17 @@ const (
6972
backoffSteps = 10
7073

7174
defaultFSType = "ext4"
75+
76+
snapshotKind = "VolumeSnapshot"
77+
snapshotAPIGroup = snapapi.GroupName // "snapshot.storage.k8s.io"
7278
)
7379

7480
// CSIProvisioner struct
7581
type csiProvisioner struct {
7682
client kubernetes.Interface
7783
csiClient csi.ControllerClient
7884
grpcClient *grpc.ClientConn
85+
snapshotClient snapclientset.Interface
7986
timeout time.Duration
8087
identity string
8188
volumeNamePrefix string
@@ -213,20 +220,49 @@ func supportsControllerCreateVolume(conn *grpc.ClientConn, timeout time.Duration
213220
return false, nil
214221
}
215222

223+
func supportsControllerCreateSnapshot(conn *grpc.ClientConn, timeout time.Duration) (bool, error) {
224+
ctx, cancel := context.WithTimeout(context.Background(), timeout)
225+
defer cancel()
226+
227+
client := csi.NewControllerClient(conn)
228+
req := csi.ControllerGetCapabilitiesRequest{}
229+
230+
rsp, err := client.ControllerGetCapabilities(ctx, &req)
231+
if err != nil {
232+
return false, err
233+
}
234+
caps := rsp.GetCapabilities()
235+
for _, cap := range caps {
236+
if cap == nil {
237+
continue
238+
}
239+
rpc := cap.GetRpc()
240+
if rpc == nil {
241+
continue
242+
}
243+
if rpc.GetType() == csi.ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT {
244+
return true, nil
245+
}
246+
}
247+
return false, nil
248+
}
249+
216250
// NewCSIProvisioner creates new CSI provisioner
217251
func NewCSIProvisioner(client kubernetes.Interface,
218252
csiEndpoint string,
219253
connectionTimeout time.Duration,
220254
identity string,
221255
volumeNamePrefix string,
222256
volumeNameUUIDLength int,
223-
grpcClient *grpc.ClientConn) controller.Provisioner {
257+
grpcClient *grpc.ClientConn,
258+
snapshotClient snapclientset.Interface) controller.Provisioner {
224259

225260
csiClient := csi.NewControllerClient(grpcClient)
226261
provisioner := &csiProvisioner{
227262
client: client,
228263
grpcClient: grpcClient,
229264
csiClient: csiClient,
265+
snapshotClient: snapshotClient,
230266
timeout: connectionTimeout,
231267
identity: identity,
232268
volumeNamePrefix: volumeNamePrefix,
@@ -238,7 +274,7 @@ func NewCSIProvisioner(client kubernetes.Interface,
238274
// This function get called before any attepmt to communicate with the driver.
239275
// Before initiating Create/Delete API calls provisioner checks if Capabilities:
240276
// PluginControllerService, ControllerCreateVolume sre supported and gets the driver name.
241-
func checkDriverState(grpcClient *grpc.ClientConn, timeout time.Duration) (string, error) {
277+
func checkDriverState(grpcClient *grpc.ClientConn, timeout time.Duration, needSnapshotSupport bool) (string, error) {
242278
ok, err := supportsPluginControllerService(grpcClient, timeout)
243279
if err != nil {
244280
glog.Errorf("failed to get support info :%v", err)
@@ -257,6 +293,24 @@ func checkDriverState(grpcClient *grpc.ClientConn, timeout time.Duration) (strin
257293
glog.Error("no create/delete volume support detected")
258294
return "", fmt.Errorf("no create/delete volume support detected")
259295
}
296+
297+
// If PVC.Spec.DataSource is not nil, it indicates the request is to create volume
298+
// from snapshot and therefore we should check for snapshot support;
299+
// otherwise we don't need to check for snapshot support.
300+
if needSnapshotSupport {
301+
// Check whether plugin supports create snapshot
302+
// If not, create volume from snapshot cannot proceed
303+
ok, err = supportsControllerCreateSnapshot(grpcClient, timeout)
304+
if err != nil {
305+
glog.Errorf("failed to get support info :%v", err)
306+
return "", err
307+
}
308+
if !ok {
309+
glog.Error("no create/delete snapshot support detected. Cannot create volume from shapshot")
310+
return "", fmt.Errorf("no create/delete snapshot support detected. Cannot create volume from shapshot")
311+
}
312+
}
313+
260314
driverName, err := getDriverName(grpcClient, timeout)
261315
if err != nil {
262316
glog.Errorf("failed to get driver info :%v", err)
@@ -289,7 +343,21 @@ func (p *csiProvisioner) Provision(options controller.VolumeOptions) (*v1.Persis
289343
return nil, fmt.Errorf("claim Selector is not supported")
290344
}
291345

292-
driverName, err := checkDriverState(p.grpcClient, p.timeout)
346+
var needSnapshotSupport bool = false
347+
if options.PVC.Spec.DataSource != nil {
348+
// PVC.Spec.DataSource.Name is the name of the VolumeSnapshot API object
349+
if options.PVC.Spec.DataSource.Name == "" {
350+
return nil, fmt.Errorf("the PVC source not found for PVC %s", options.PVC.Name)
351+
}
352+
if options.PVC.Spec.DataSource.Kind != snapshotKind {
353+
return nil, fmt.Errorf("the PVC source is not the right type. Expected %s, Got %s", snapshotKind, options.PVC.Spec.DataSource.Kind)
354+
}
355+
if options.PVC.Spec.DataSource.APIGroup != snapshotAPIGroup {
356+
return nil, fmt.Errorf("the PVC source does not belong to the right APIGroup. Expected %s, Got %s", snapshotAPIGroup, options.PVC.Spec.DataSource.APIGroup)
357+
}
358+
needSnapshotSupport = true
359+
}
360+
driverName, err := checkDriverState(p.grpcClient, p.timeout, needSnapshotSupport)
293361
if err != nil {
294362
return nil, err
295363
}
@@ -317,6 +385,17 @@ func (p *csiProvisioner) Provision(options controller.VolumeOptions) (*v1.Persis
317385
RequiredBytes: int64(volSizeBytes),
318386
},
319387
}
388+
389+
if needSnapshotSupport {
390+
volumeContentSource, err := p.getVolumeContentSource(options)
391+
if err != nil {
392+
return nil, fmt.Errorf("error getting snapshot handle for snapshot %s: %v", options.PVC.Spec.DataSource.Name, err)
393+
}
394+
req.VolumeContentSource = volumeContentSource
395+
}
396+
397+
glog.V(5).Infof("CreateVolumeRequest %+v", req)
398+
320399
rep := &csi.CreateVolumeResponse{}
321400

322401
// Resolve provision secret credentials.
@@ -433,13 +512,65 @@ func (p *csiProvisioner) Provision(options controller.VolumeOptions) (*v1.Persis
433512
return pv, nil
434513
}
435514

515+
func (p *csiProvisioner) getVolumeContentSource(options controller.VolumeOptions) (*csi.VolumeContentSource, error) {
516+
snapshotObj, err := p.snapshotClient.VolumesnapshotV1alpha1().VolumeSnapshots(options.PVC.Namespace).Get(options.PVC.Spec.DataSource.Name, metav1.GetOptions{})
517+
if err != nil {
518+
return nil, fmt.Errorf("error getting snapshot %s from api server: %v", options.PVC.Spec.DataSource.Name, err)
519+
}
520+
if snapshotObj.Status.Ready == false {
521+
return nil, fmt.Errorf("snapshot %s is not Ready", options.PVC.Spec.DataSource.Name)
522+
}
523+
524+
glog.V(5).Infof("VolumeSnapshot %+v", snapshotObj)
525+
526+
snapContentObj, err := p.snapshotClient.VolumesnapshotV1alpha1().VolumeSnapshotContents().Get(snapshotObj.Spec.SnapshotContentName, metav1.GetOptions{})
527+
if err != nil {
528+
return nil, fmt.Errorf("error getting snapshot:snapshotcontent %s:%s from api server: %v", snapshotObj.Name, snapshotObj.Spec.SnapshotContentName, err)
529+
}
530+
glog.V(5).Infof("VolumeSnapshotContent %+v", snapContentObj)
531+
532+
if snapContentObj.Spec.VolumeSnapshotSource.CSI == nil {
533+
return nil, fmt.Errorf("error getting snapshot source from snapshot:snapshotcontent %s:%s", snapshotObj.Name, snapshotObj.Spec.SnapshotContentName)
534+
}
535+
536+
snapshotSource := csi.VolumeContentSource_Snapshot{
537+
Snapshot: &csi.VolumeContentSource_SnapshotSource{
538+
Id: snapContentObj.Spec.VolumeSnapshotSource.CSI.SnapshotHandle,
539+
},
540+
}
541+
glog.V(5).Infof("VolumeContentSource_Snapshot %+v", snapshotSource)
542+
543+
if snapshotObj.Status.RestoreSize != nil {
544+
capacity, exists := options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
545+
if !exists {
546+
return nil, fmt.Errorf("error getting capacity for PVC %s when creating snapshot %s", options.PVC.Name, snapshotObj.Name)
547+
}
548+
volSizeBytes := capacity.Value()
549+
glog.V(5).Infof("Requested volume size is %d and snapshot size is %d for the source snapshot %s", int64(volSizeBytes), int64(snapshotObj.Status.RestoreSize.Value()), snapshotObj.Name)
550+
// When restoring volume from a snapshot, the volume size should
551+
// be equal to or larger than its snapshot size.
552+
if int64(volSizeBytes) < int64(snapshotObj.Status.RestoreSize.Value()) {
553+
return nil, fmt.Errorf("requested volume size %d is less than the size %d for the source snapshot %s", int64(volSizeBytes), int64(snapshotObj.Status.RestoreSize.Value()), snapshotObj.Name)
554+
}
555+
if int64(volSizeBytes) > int64(snapshotObj.Status.RestoreSize.Value()) {
556+
glog.Warningf("requested volume size %d is greater than the size %d for the source snapshot %s. Volume plugin needs to handle volume expansion.", int64(volSizeBytes), int64(snapshotObj.Status.RestoreSize.Value()), snapshotObj.Name)
557+
}
558+
}
559+
560+
volumeContentSource := &csi.VolumeContentSource{
561+
Type: &snapshotSource,
562+
}
563+
564+
return volumeContentSource, nil
565+
}
566+
436567
func (p *csiProvisioner) Delete(volume *v1.PersistentVolume) error {
437568
if volume == nil || volume.Spec.CSI == nil {
438569
return fmt.Errorf("invalid CSI PV")
439570
}
440571
volumeId := p.volumeHandleToId(volume.Spec.CSI.VolumeHandle)
441572

442-
_, err := checkDriverState(p.grpcClient, p.timeout)
573+
_, err := checkDriverState(p.grpcClient, p.timeout, false)
443574
if err != nil {
444575
return err
445576
}

0 commit comments

Comments
 (0)