Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions mock/service/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,33 @@ func (s *service) CreateVolume(
if capacity >= MaxStorageCapacity {
return nil, status.Errorf(codes.OutOfRange, "Requested capacity %d exceeds maximum allowed %d", capacity, MaxStorageCapacity)
}
// Create the volume and add it to the service's in-mem volume slice.
v := s.newVolume(req.Name, capacity)

var v csi.Volume
// Create volume from content source if provided.
if req.GetVolumeContentSource() != nil {
switch req.GetVolumeContentSource().GetType().(type) {
case *csi.VolumeContentSource_Snapshot:
sid := req.GetVolumeContentSource().GetSnapshot().GetSnapshotId()
// Check if the source snapshot exists.
if snapID, _ := s.snapshots.FindSnapshot("id", sid); snapID >= 0 {
v = s.newVolumeFromSnapshot(req.Name, capacity, snapID)
} else {
return nil, status.Errorf(codes.NotFound, "Requested source snapshot %s not found", sid)
}
case *csi.VolumeContentSource_Volume:
vid := req.GetVolumeContentSource().GetVolume().GetVolumeId()
// Check if the source volume exists.
if volID, _ := s.findVolNoLock("id", vid); volID > 0 {
v = s.newVolumeFromVolume(req.Name, capacity, volID)
} else {
return nil, status.Errorf(codes.NotFound, "Requested source volume %s not found", vid)
}
}
} else {
v = s.newVolume(req.Name, capacity)
}

// Add the created volume to the service's in-mem volume slice.
s.volsRWL.Lock()
defer s.volsRWL.Unlock()
s.vols = append(s.vols, v)
Expand Down Expand Up @@ -398,6 +423,13 @@ func (s *service) ControllerGetCapabilities(
},
},
},
{
Type: &csi.ControllerServiceCapability_Rpc{
Rpc: &csi.ControllerServiceCapability_RPC{
Type: csi.ControllerServiceCapability_RPC_CLONE_VOLUME,
},
},
},
}

if !s.config.DisableAttach {
Expand Down
24 changes: 24 additions & 0 deletions mock/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,30 @@ func (s *service) newVolume(name string, capcity int64) csi.Volume {
}
}

func (s *service) newVolumeFromSnapshot(name string, capacity int64, snapshotID int) csi.Volume {
vol := s.newVolume(name, capacity)
vol.ContentSource = &csi.VolumeContentSource{
Type: &csi.VolumeContentSource_Snapshot{
Snapshot: &csi.VolumeContentSource_SnapshotSource{
SnapshotId: string(snapshotID),
},
},
}
return vol
}

func (s *service) newVolumeFromVolume(name string, capacity int64, volumeID int) csi.Volume {
vol := s.newVolume(name, capacity)
vol.ContentSource = &csi.VolumeContentSource{
Type: &csi.VolumeContentSource_Volume{
Volume: &csi.VolumeContentSource_VolumeSource{
VolumeId: string(volumeID),
},
},
}
return vol
}

func (s *service) findVol(k, v string) (volIdx int, volInfo csi.Volume) {
s.volsRWL.RLock()
defer s.volsRWL.RUnlock()
Expand Down
127 changes: 127 additions & 0 deletions pkg/sanity/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,133 @@ var _ = DescribeSanity("Controller Service", func(sc *SanityContext) {
Expect(err).NotTo(HaveOccurred())
cl.UnregisterVolume(name)
})

It("should create volume from an existing source snapshot", func() {
if !isControllerCapabilitySupported(c, csi.ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT) {
Skip("Snapshot not supported")
}

By("creating a volume")
vol1Name := uniqueString("sanity-controller-source-vol")
vol1Req := MakeCreateVolumeReq(sc, vol1Name)
volume1, err := c.CreateVolume(context.Background(), vol1Req)
Expect(err).NotTo(HaveOccurred())

By("creating a snapshot")
snapName := uniqueString("sanity-controller-snap-from-vol")
snapReq := MakeCreateSnapshotReq(sc, snapName, volume1.GetVolume().GetVolumeId(), nil)
snap, err := c.CreateSnapshot(context.Background(), snapReq)
Expect(err).NotTo(HaveOccurred())
Expect(snap).NotTo(BeNil())
verifySnapshotInfo(snap.GetSnapshot())

By("creating a volume from source snapshot")
vol2Name := uniqueString("sanity-controller-vol-from-snap")
vol2Req := MakeCreateVolumeReq(sc, vol2Name)
vol2Req.VolumeContentSource = &csi.VolumeContentSource{
Type: &csi.VolumeContentSource_Snapshot{
Snapshot: &csi.VolumeContentSource_SnapshotSource{
SnapshotId: snap.GetSnapshot().GetSnapshotId(),
},
},
}
volume2, err := c.CreateVolume(context.Background(), vol2Req)
Expect(err).NotTo(HaveOccurred())

By("cleaning up deleting the volume created from snapshot")
delVol2Req := MakeDeleteVolumeReq(sc, volume2.GetVolume().GetVolumeId())
_, err = c.DeleteVolume(context.Background(), delVol2Req)
Expect(err).NotTo(HaveOccurred())

By("cleaning up deleting the snapshot")
delSnapReq := MakeDeleteSnapshotReq(sc, snap.GetSnapshot().GetSnapshotId())
_, err = c.DeleteSnapshot(context.Background(), delSnapReq)
Expect(err).NotTo(HaveOccurred())

By("cleaning up deleting the source volume")
delVol1Req := MakeDeleteVolumeReq(sc, volume1.GetVolume().GetVolumeId())
_, err = c.DeleteVolume(context.Background(), delVol1Req)
Expect(err).NotTo(HaveOccurred())
})

It("should fail when the volume source snapshot is not found", func() {
if !isControllerCapabilitySupported(c, csi.ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT) {
Skip("Snapshot not supported")
}

By("creating a volume from source snapshot")
volName := uniqueString("sanity-controller-vol-from-snap")
volReq := MakeCreateVolumeReq(sc, volName)
volReq.VolumeContentSource = &csi.VolumeContentSource{
Type: &csi.VolumeContentSource_Snapshot{
Snapshot: &csi.VolumeContentSource_SnapshotSource{
SnapshotId: "non-existing-snapshot-id",
},
},
}
_, err := c.CreateVolume(context.Background(), volReq)
Expect(err).To(HaveOccurred())
serverError, ok := status.FromError(err)
Expect(ok).To(BeTrue())
Expect(serverError.Code()).To(Equal(codes.NotFound))
})

It("should create volume from an existing source volume", func() {
if !isControllerCapabilitySupported(c, csi.ControllerServiceCapability_RPC_CLONE_VOLUME) {
Skip("Volume Cloning not supported")
}

By("creating a volume")
vol1Name := uniqueString("sanity-controller-source-vol")
vol1Req := MakeCreateVolumeReq(sc, vol1Name)
volume1, err := c.CreateVolume(context.Background(), vol1Req)
Expect(err).NotTo(HaveOccurred())

By("creating a volume from source volume")
vol2Name := uniqueString("sanity-controller-vol-from-vol")
vol2Req := MakeCreateVolumeReq(sc, vol2Name)
vol2Req.VolumeContentSource = &csi.VolumeContentSource{
Type: &csi.VolumeContentSource_Volume{
Volume: &csi.VolumeContentSource_VolumeSource{
VolumeId: volume1.GetVolume().GetVolumeId(),
},
},
}
volume2, err := c.CreateVolume(context.Background(), vol2Req)
Expect(err).NotTo(HaveOccurred())

By("cleaning up deleting the volume created from source volume")
delVol2Req := MakeDeleteVolumeReq(sc, volume2.GetVolume().GetVolumeId())
_, err = c.DeleteVolume(context.Background(), delVol2Req)
Expect(err).NotTo(HaveOccurred())

By("cleaning up deleting the source volume")
delVol1Req := MakeDeleteVolumeReq(sc, volume1.GetVolume().GetVolumeId())
_, err = c.DeleteVolume(context.Background(), delVol1Req)
Expect(err).NotTo(HaveOccurred())
})

It("should fail when the volume source volume is not found", func() {
if !isControllerCapabilitySupported(c, csi.ControllerServiceCapability_RPC_CLONE_VOLUME) {
Skip("Volume Cloning not supported")
}

By("creating a volume from source snapshot")
volName := uniqueString("sanity-controller-vol-from-snap")
volReq := MakeCreateVolumeReq(sc, volName)
volReq.VolumeContentSource = &csi.VolumeContentSource{
Type: &csi.VolumeContentSource_Volume{
Volume: &csi.VolumeContentSource_VolumeSource{
VolumeId: "non-existing-volume-id",
},
},
}
_, err := c.CreateVolume(context.Background(), volReq)
Expect(err).To(HaveOccurred())
serverError, ok := status.FromError(err)
Expect(ok).To(BeTrue())
Expect(serverError.Code()).To(Equal(codes.NotFound))
})
})

Describe("DeleteVolume", func() {
Expand Down