@@ -19,6 +19,7 @@ package hostpath
1919import (
2020 "fmt"
2121 "os"
22+ "time"
2223
2324 "github.com/golang/glog"
2425 "github.com/pborman/uuid"
@@ -28,11 +29,13 @@ import (
2829
2930 "github.com/container-storage-interface/spec/lib/go/csi/v0"
3031 "github.com/kubernetes-csi/drivers/pkg/csi-common"
32+ utilexec "k8s.io/utils/exec"
3133)
3234
3335const (
3436 deviceID = "deviceID"
3537 provisionRoot = "/tmp/"
38+ snapshotRoot = "/tmp/"
3639 maxStorageCapacity = tib
3740)
3841
@@ -136,3 +139,151 @@ func (cs *controllerServer) ValidateVolumeCapabilities(ctx context.Context, req
136139 }
137140 return & csi.ValidateVolumeCapabilitiesResponse {Supported : true , Message : "" }, nil
138141}
142+
143+ // CreateSnapshot uses tar command to create snapshot for hostpath volume. The tar command can quickly create
144+ // archives of entire directories. The host image must have "tar" binaries in /bin, /usr/sbin, or /usr/bin.
145+ func (cs * controllerServer ) CreateSnapshot (ctx context.Context , req * csi.CreateSnapshotRequest ) (* csi.CreateSnapshotResponse , error ) {
146+ if err := cs .Driver .ValidateControllerServiceRequest (csi .ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT ); err != nil {
147+ glog .V (3 ).Infof ("invalid create snapshot req: %v" , req )
148+ return nil , err
149+ }
150+
151+ // Check arguments
152+ if len (req .GetSourceVolumeId ()) == 0 {
153+ return nil , status .Error (codes .InvalidArgument , "SourceVolumeId missing in request" )
154+ }
155+ if len (req .GetName ()) == 0 {
156+ return nil , status .Error (codes .InvalidArgument , "Name missing in request" )
157+ }
158+
159+ // Need to check for already existing snapshot name, and if found check for the
160+ // requested sourceVolumeId and sourceVolumeId of snapshot that has been created.
161+ if exSnap , err := getSnapshotByName (req .GetName ()); err == nil {
162+ // Since err is nil, it means the snapshot with the same name already exists need
163+ // to check if the sourceVolumeId of existing snapshot is the same as in new request.
164+ if exSnap .VolID == req .GetSourceVolumeId () {
165+ // same snapshot has been created.
166+ return & csi.CreateSnapshotResponse {
167+ Snapshot : & csi.Snapshot {
168+ Id : exSnap .Id ,
169+ SourceVolumeId : exSnap .VolID ,
170+ CreatedAt : exSnap .CreateAt ,
171+ Status : exSnap .Status ,
172+ },
173+ }, nil
174+ }
175+ return nil , status .Error (codes .AlreadyExists , fmt .Sprintf ("snapshot with the same name: %s but with different SourceVolumeId already exist" , req .GetName ()))
176+ }
177+
178+ volumeID := req .GetSourceVolumeId ()
179+ hostPathVolume , ok := hostPathVolumes [volumeID ]
180+ if ! ok {
181+ return nil , status .Error (codes .Internal , "volumeID is not exist" )
182+ }
183+
184+ snapshotID := uuid .NewUUID ().String ()
185+ createAt := time .Now ().UnixNano ()
186+ volPath := hostPathVolume .VolPath
187+ file := snapshotRoot + snapshotID + ".tgz"
188+ args := []string {"czf" , file , "-C" , volPath , "." }
189+ executor := utilexec .New ()
190+ out , err := executor .Command ("tar" , args ... ).CombinedOutput ()
191+ if err != nil {
192+ return nil , status .Error (codes .Internal , fmt .Sprintf ("failed create snapshot: %v: %s" , err , out ))
193+ }
194+
195+ glog .V (4 ).Infof ("create volume snapshot %s" , file )
196+ snapshot := hostPathSnapshot {}
197+ snapshot .Name = req .GetName ()
198+ snapshot .Id = snapshotID
199+ snapshot .VolID = volumeID
200+ snapshot .Path = file
201+ snapshot .CreateAt = createAt
202+ snapshot .Status = & csi.SnapshotStatus {
203+ Type : csi .SnapshotStatus_READY ,
204+ Details : fmt .Sprint ("Successfully create snapshot" ),
205+ }
206+
207+ hostPathVolumeSnapshots [snapshotID ] = snapshot
208+
209+ return & csi.CreateSnapshotResponse {
210+ Snapshot : & csi.Snapshot {
211+ Id : snapshot .Id ,
212+ SourceVolumeId : snapshot .VolID ,
213+ CreatedAt : snapshot .CreateAt ,
214+ Status : snapshot .Status ,
215+ },
216+ }, nil
217+ }
218+
219+ func (cs * controllerServer ) DeleteSnapshot (ctx context.Context , req * csi.DeleteSnapshotRequest ) (* csi.DeleteSnapshotResponse , error ) {
220+ // Check arguments
221+ if len (req .GetSnapshotId ()) == 0 {
222+ return nil , status .Error (codes .InvalidArgument , "Snapshot ID missing in request" )
223+ }
224+
225+ if err := cs .Driver .ValidateControllerServiceRequest (csi .ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT ); err != nil {
226+ glog .V (3 ).Infof ("invalid delete snapshot req: %v" , req )
227+ return nil , err
228+ }
229+ snapshotID := req .SnapshotId
230+ glog .V (4 ).Infof ("deleting volume %s" , snapshotID )
231+ path := snapshotRoot + snapshotID + ".tgz"
232+ os .RemoveAll (path )
233+ delete (hostPathVolumeSnapshots , snapshotID )
234+ return & csi.DeleteSnapshotResponse {}, nil
235+ }
236+
237+ func (cs * controllerServer ) ListSnapshots (ctx context.Context , req * csi.ListSnapshotsRequest ) (* csi.ListSnapshotsResponse , error ) {
238+ if err := cs .Driver .ValidateControllerServiceRequest (csi .ControllerServiceCapability_RPC_LIST_SNAPSHOTS ); err != nil {
239+ glog .V (3 ).Infof ("invalid list snapshot req: %v" , req )
240+ return nil , err
241+ }
242+
243+ var snapshots []hostPathSnapshot
244+
245+ // case 1: SnapshotId is not empty, return snapshots that match the snapshot id.
246+ if len (req .GetSnapshotId ()) != 0 {
247+ snapshotID := req .SnapshotId
248+ if snapshot , ok := hostPathVolumeSnapshots [snapshotID ]; ok {
249+ snapshots = append (snapshots , snapshot )
250+ // jump to the result processing.
251+ goto result
252+ }
253+ }
254+
255+ // case 2: SourceVolumeId is not empty, return snapshots that match the source volume id.
256+ if len (req .SourceVolumeId ) != 0 {
257+ for _ , snapshot := range hostPathVolumeSnapshots {
258+ if snapshot .VolID == req .SourceVolumeId {
259+ snapshots = append (snapshots , snapshot )
260+ // jump to the result processing.
261+ goto result
262+ }
263+ }
264+ }
265+
266+ // case 3: no parameter is set, so we return all the snapshots.
267+ for _ , snapshot := range hostPathVolumeSnapshots {
268+ snapshots = append (snapshots , snapshot )
269+ }
270+
271+ result:
272+ var entries []* csi.ListSnapshotsResponse_Entry
273+ for _ , snap := range snapshots {
274+ entries = append (entries , & csi.ListSnapshotsResponse_Entry {
275+ Snapshot : & csi.Snapshot {
276+ Id : snap .Id ,
277+ SourceVolumeId : snap .VolID ,
278+ CreatedAt : snap .CreateAt ,
279+ Status : snap .Status ,
280+ },
281+ })
282+ }
283+
284+ rsp := & csi.ListSnapshotsResponse {
285+ Entries : entries ,
286+ }
287+
288+ return rsp , nil
289+ }
0 commit comments