Skip to content

Commit

Permalink
Add RestoreFileSystemFromSnapshot functionality for csi-powerflex nfs…
Browse files Browse the repository at this point in the history
…-support (#74)

* add support for restore fs from snapshot.

* resolved the conflicts.

* add debug logs.

* add debug logs.

* added unit test and int tests.
  • Loading branch information
VamsiSiddu-7 authored Jul 25, 2023
1 parent 8f8b41a commit 12217ad
Show file tree
Hide file tree
Showing 4 changed files with 262 additions and 0 deletions.
29 changes: 29 additions & 0 deletions fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,35 @@ func (s *System) CreateFileSystemSnapshot(createSnapParam *types.CreateFileSyste
return &snapResponse, nil
}

// RestoreFileSystemFromSnapshot restores the filesystem from a given snapshot using filesytem id
func (s *System) RestoreFileSystemFromSnapshot(restoreSnapParam *types.RestoreFsSnapParam, fsID string) (*types.RestoreFsSnapResponse, error) {
defer TimeSpent("CreateFileSystemSnapshot", time.Now())

path := fmt.Sprintf("/rest/v1/file-systems/%v/restore", fsID)

restoreFsResponse := types.RestoreFsSnapResponse{}
var err error
if restoreSnapParam.CopyName == "" {
err = s.client.getJSONWithRetry(
http.MethodPost, path, restoreSnapParam, nil)
if err == nil {
return nil, nil
}
} else {
err = s.client.getJSONWithRetry(
http.MethodPost, path, restoreSnapParam, &restoreFsResponse)
if err == nil {
return &restoreFsResponse, nil
}
}

if err != nil {
return nil, err
}

return nil, nil
}

// DeleteFileSystem deletes a file system
func (s *System) DeleteFileSystem(name string) error {
defer TimeSpent("DeleteFileSystem", time.Now())
Expand Down
138 changes: 138 additions & 0 deletions fs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,144 @@ func TestCreateFileSystemSnapshot(t *testing.T) {
}
}

func TestRestoreFileSystemFromSnapshot(t *testing.T) {
type checkFn func(*testing.T, *types.RestoreFsSnapResponse, error)
check := func(fns ...checkFn) []checkFn { return fns }

hasNoError := func(t *testing.T, resp *types.RestoreFsSnapResponse, err error) {
if err != nil {
t.Fatalf("expected no error")
}
}

hasError := func(t *testing.T, resp *types.RestoreFsSnapResponse, err error) {
if err == nil {
t.Fatalf("expected error")
}
}

checkResp := func(snapId string) func(t *testing.T, resp *types.RestoreFsSnapResponse, err error) {
return func(t *testing.T, resp *types.RestoreFsSnapResponse, err error) {
assert.Equal(t, snapId, resp.ID)
}
}

tests := map[string]func(t *testing.T) (*httptest.Server, []checkFn){
"successNoContent": func(t *testing.T) (*httptest.Server, []checkFn) {

href := fmt.Sprintf("/rest/v1/file-systems/%v/restore", "64366a19-54e8-1544-f3d7-2a50fb1ccff3")

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
t.Fatal(fmt.Errorf("wrong method. Expected %s; but got %s", http.MethodGet, r.Method))
}

if r.URL.Path != href {
t.Fatal(fmt.Errorf("wrong path. Expected %s; but got %s", href, r.URL.Path))
}

w.WriteHeader(http.StatusNoContent)
}))
return ts, check(hasNoError)
},

"successWithContent": func(t *testing.T) (*httptest.Server, []checkFn) {

href := fmt.Sprintf("/rest/v1/file-systems/%v/restore", "64366a19-54e8-1544-f3d7-2a50fb1ccff3")

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
t.Fatal(fmt.Errorf("wrong method. Expected %s; but got %s", http.MethodGet, r.Method))
}

if r.URL.Path != href {
t.Fatal(fmt.Errorf("wrong path. Expected %s; but got %s", href, r.URL.Path))
}

resp := types.RestoreFsSnapResponse{
ID: "64366a19-54e8-1544-f3d7-2a50fb1cckk3",
}

respData, err := json.Marshal(resp)
if err != nil {
t.Fatal(err)
}
fmt.Fprintln(w, string(respData))
}))
return ts, check(hasNoError, checkResp("64366a19-54e8-1544-f3d7-2a50fb1cckk3"))
},
"not-found": func(t *testing.T) (*httptest.Server, []checkFn) {
href := fmt.Sprintf("/rest/v1/file-systems/%v/restore", "64366a19-54e8-1544-f3d7-2a50fb1ccff3")

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
t.Fatal(fmt.Errorf("wrong method. Expected %s; but got %s", http.MethodGet, r.Method))
}

if r.URL.Path != href {
t.Fatal(fmt.Errorf("wrong path. Expected %s; but got %s", href, r.URL.Path))
}

http.Error(w, "not found", http.StatusNotFound)
}))
return ts, check(hasError)
},

"operation-failed": func(t *testing.T) (*httptest.Server, []checkFn) {
href := fmt.Sprintf("/rest/v1/file-systems/%v/restore", "64366a19-54e8-1544-f3d7-2a50fb1ccff3")

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
t.Fatal(fmt.Errorf("wrong method. Expected %s; but got %s", http.MethodGet, r.Method))
}

if r.URL.Path != href {
t.Fatal(fmt.Errorf("wrong path. Expected %s; but got %s", href, r.URL.Path))
}

http.Error(w, "operation failed", http.StatusUnprocessableEntity)
}))
return ts, check(hasError)
},
}

for name, tc := range tests {
t.Run(name, func(t *testing.T) {
ts, checkFns := tc(t)
defer ts.Close()

client, err := NewClientWithArgs(ts.URL, "", math.MaxInt64, true, false)
client.configConnect.Version = "4.0"
if err != nil {
t.Fatal(err)
}

s := System{
client: client,
}

fsID := "64366a19-54e8-1544-f3d7-2a50fb1ccff3"
var restoreSnapshotRequest = new(types.RestoreFsSnapParam)
if name == "successWithContent" {
restoreSnapshotRequest = &types.RestoreFsSnapParam{
SnapshotID: "64366a19-54e8-1544-f3d7-2a50fb1ccdd3",
CopyName: "test-name",
}
} else {
restoreSnapshotRequest = &types.RestoreFsSnapParam{
SnapshotID: "64366a19-54e8-1544-f3d7-2a50fb1ccdd3",
}

}

resp, err := s.RestoreFileSystemFromSnapshot(restoreSnapshotRequest, fsID)
for _, checkFn := range checkFns {
checkFn(t, resp, err)
}

})
}
}
func TestDeleteFileSystem(t *testing.T) {

name := "new-fs"
Expand Down
84 changes: 84 additions & 0 deletions inttests/fs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,90 @@ func TestCreateFileSystemSnapshot(t *testing.T) {

}

func TestRestoreFileSystemSnapshot(t *testing.T) {
system := getSystem()
assert.NotNil(t, system)

fsName := fmt.Sprintf("%s-%s", "FS", testPrefix)

// get protection domain
pd := getProtectionDomain(t)
assert.NotNil(t, pd)

// get storage pool
pool := getStoragePool(t)
assert.NotNil(t, pool)
var spID string
if pd != nil && pool != nil {
sp, _ := pd.FindStoragePool(pool.StoragePool.ID, "", "")
assert.NotNil(t, sp)
spID = sp.ID
}

// get NAS server ID
var nasServerName string
if os.Getenv("GOSCALEIO_NASSERVER") != "" {
nasServerName = os.Getenv("GOSCALEIO_NASSERVER")
}
nasServer, err := system.GetNASByIDName("", nasServerName)
assert.NotNil(t, nasServer)
assert.Nil(t, err)

fs := &types.FsCreate{
Name: fsName,
SizeTotal: 16106127360,
StoragePoolID: spID,
NasServerID: nasServer.ID,
}

// create the file system
filesystem, err := system.CreateFileSystem(fs)
fsID := filesystem.ID
assert.Nil(t, err)
assert.NotNil(t, fsID)
snapName := fmt.Sprintf("%s-%s", "SNAP", testPrefix)

snapResp, err := system.CreateFileSystemSnapshot(&types.CreateFileSystemSnapshotParam{
Name: snapName,
}, fsID)

assert.NotNil(t, snapResp)
assert.Nil(t, err)

snap, err := system.GetFileSystemByIDName(snapResp.ID, "")
assert.NotNil(t, snap)
assert.Nil(t, err)

restoreSnap, err := system.RestoreFileSystemFromSnapshot(&types.RestoreFsSnapParam{
SnapshotID: snap.ID,
}, fsID)

assert.Nil(t, restoreSnap)
assert.Nil(t, err)

restoreSnap, err = system.RestoreFileSystemFromSnapshot(&types.RestoreFsSnapParam{
SnapshotID: snap.ID,
CopyName: "copy" + snapName,
}, fsID)

assert.NotNil(t, restoreSnap)
assert.Nil(t, err)

rSnap, err := system.GetFileSystemByIDName(restoreSnap.ID, "")

assert.NotNil(t, rSnap)
assert.Nil(t, err)

err = system.DeleteFileSystem(rSnap.Name)
assert.Nil(t, err)

err = system.DeleteFileSystem(snap.Name)
assert.Nil(t, err)
err = system.DeleteFileSystem(fs.Name)
assert.Nil(t, err)

}

// TestGetFileSystemByIDName will return specific filesystem by name or ID
func TestGetFileSystemByIDName(t *testing.T) {

Expand Down
11 changes: 11 additions & 0 deletions types/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1365,6 +1365,17 @@ type FileSystemResp struct {
ID string `json:"id"`
}

// RestoreFsSnapParam defines struct for Restoring filesytem from snapshot
type RestoreFsSnapParam struct {
SnapshotID string `json:"snapshot_id"`
CopyName string `json:"copy_name,omitempty"`
}

// RestoreFsSnapResponse defines struct for Restore Filesystem snapshot response
type RestoreFsSnapResponse struct {
ID string `json:"id"`
}

// NFSExportDefaultAccessEnum defines default access
type NFSExportDefaultAccessEnum string

Expand Down

0 comments on commit 12217ad

Please sign in to comment.